/**
 * Registration Form Validation Test
 * Tests that the username field correctly validates input while typing
 * Specifically checks that numbers don't incorrectly trigger validation warnings
 * CI-compatible: Works in both local and CI environments
 */

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

// NAVIGATION NOTE: Using 'domcontentloaded' instead of 'networkidle2' for page.goto()
// because networkidle2 waits for no network activity for 500ms, but WebSocket
// connections and background polling keep the network active, causing infinite hangs.
// See: test_login_validation.js and auth_helper.js for detailed explanation.
async function testRegisterValidation() {
    const isCI = !!process.env.CI;
    console.log(`🧪 Running registration form validation test (CI mode: ${isCI})`);

    // Create screenshots directory if it doesn't exist
    const screenshotsDir = path.join(__dirname, 'screenshots');
    if (!fs.existsSync(screenshotsDir)) {
        fs.mkdirSync(screenshotsDir, { recursive: true });
    }

    const browser = await puppeteer.launch(getPuppeteerLaunchOptions());
    const page = await browser.newPage();
    const baseUrl = 'http://127.0.0.1:5000';

    // Increase default timeout in CI
    if (isCI) {
        page.setDefaultTimeout(60000);
        page.setDefaultNavigationTimeout(60000);
    }

    let testsPassed = 0;
    let testsFailed = 0;

    console.log('🧪 Starting registration form validation tests...\n');

    try {
        // Navigate to register page
        console.log('📄 Navigating to registration page...');
        await page.goto(`${baseUrl}/auth/register`, {
            waitUntil: 'domcontentloaded',
            timeout: 30000
        });

        // Wait for the username input
        await page.waitForSelector('input[name="username"]', { timeout: 10000 });
        console.log('✅ Registration page loaded\n');

        // Test 1: Check CSS class consistency
        console.log('📋 Test 1: CSS class consistency');
        const usernameInput = await page.$('input[name="username"]');
        const usernameClass = await page.evaluate(el => el.className, usernameInput);

        if (usernameClass.includes('ldr-form-control')) {
            console.log('✅ Username input uses ldr-form-control class');
            testsPassed++;
        } else {
            console.log(`❌ Username input has incorrect class: "${usernameClass}" (expected ldr-form-control)`);
            testsFailed++;
        }

        // Check password fields too
        const passwordInput = await page.$('input[name="password"]');
        const passwordClass = await page.evaluate(el => el.className, passwordInput);

        if (passwordClass.includes('ldr-form-control')) {
            console.log('✅ Password input uses ldr-form-control class');
            testsPassed++;
        } else {
            console.log(`❌ Password input has incorrect class: "${passwordClass}" (expected ldr-form-control)`);
            testsFailed++;
        }

        const confirmPasswordInput = await page.$('input[name="confirm_password"]');
        const confirmPasswordClass = await page.evaluate(el => el.className, confirmPasswordInput);

        if (confirmPasswordClass.includes('ldr-form-control')) {
            console.log('✅ Confirm password input uses ldr-form-control class');
            testsPassed++;
        } else {
            console.log(`❌ Confirm password input has incorrect class: "${confirmPasswordClass}" (expected ldr-form-control)`);
            testsFailed++;
        }

        // Test 2: Check pattern attribute
        console.log('\n📋 Test 2: Pattern attribute validation');
        const pattern = await page.evaluate(el => el.pattern, usernameInput);
        console.log(`   Pattern: ${pattern}`);

        // The pattern should allow letters, numbers, underscores, and hyphens
        if (pattern && pattern.includes('a-zA-Z') && pattern.includes('0-9')) {
            console.log('✅ Pattern correctly includes letters and numbers');
            testsPassed++;
        } else {
            console.log('❌ Pattern may not correctly include letters and numbers');
            testsFailed++;
        }

        // Test 3: Type letters only - should be valid
        console.log('\n📋 Test 3: Typing letters only');
        await page.focus('input[name="username"]');
        await page.keyboard.type('testuser', { delay: 50 });

        let validity = await page.evaluate(el => ({
            valid: el.validity.valid,
            patternMismatch: el.validity.patternMismatch,
            valueMissing: el.validity.valueMissing,
            tooShort: el.validity.tooShort
        }), usernameInput);

        console.log(`   Value: "testuser", Valid: ${validity.valid}, PatternMismatch: ${validity.patternMismatch}`);

        if (validity.valid && !validity.patternMismatch) {
            console.log('✅ Letters-only username is valid');
            testsPassed++;
        } else {
            console.log('❌ Letters-only username incorrectly marked invalid');
            testsFailed++;
        }

        // Clear the input
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });

        // Test 4: Type numbers only - should be valid
        console.log('\n📋 Test 4: Typing numbers only');
        await page.focus('input[name="username"]');
        await page.keyboard.type('12345', { delay: 50 });

        validity = await page.evaluate(el => ({
            valid: el.validity.valid,
            patternMismatch: el.validity.patternMismatch,
            valueMissing: el.validity.valueMissing,
            tooShort: el.validity.tooShort
        }), usernameInput);

        console.log(`   Value: "12345", Valid: ${validity.valid}, PatternMismatch: ${validity.patternMismatch}`);

        // Note: "12345" has 5 characters and minlength is 1, so it should pass length check
        // It should NOT have a pattern mismatch since numbers are allowed
        if (!validity.patternMismatch) {
            console.log('✅ Numbers-only username does not trigger pattern mismatch');
            testsPassed++;
        } else {
            console.log('❌ Numbers-only username incorrectly triggers pattern mismatch');
            testsFailed++;
        }

        // Clear the input
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });

        // Test 5: Type mixed letters and numbers - should be valid
        console.log('\n📋 Test 5: Typing mixed letters and numbers');
        await page.focus('input[name="username"]');
        await page.keyboard.type('user123', { delay: 50 });

        validity = await page.evaluate(el => ({
            valid: el.validity.valid,
            patternMismatch: el.validity.patternMismatch,
            valueMissing: el.validity.valueMissing,
            tooShort: el.validity.tooShort
        }), usernameInput);

        console.log(`   Value: "user123", Valid: ${validity.valid}, PatternMismatch: ${validity.patternMismatch}`);

        if (validity.valid && !validity.patternMismatch) {
            console.log('✅ Mixed letters/numbers username is valid');
            testsPassed++;
        } else {
            console.log('❌ Mixed letters/numbers username incorrectly marked invalid');
            testsFailed++;
        }

        // Clear the input
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });

        // Test 6: Type with underscores and hyphens - should be valid
        console.log('\n📋 Test 6: Typing with underscores and hyphens');
        await page.focus('input[name="username"]');
        await page.keyboard.type('test_user-123', { delay: 50 });

        validity = await page.evaluate(el => ({
            valid: el.validity.valid,
            patternMismatch: el.validity.patternMismatch,
            valueMissing: el.validity.valueMissing,
            tooShort: el.validity.tooShort
        }), usernameInput);

        console.log(`   Value: "test_user-123", Valid: ${validity.valid}, PatternMismatch: ${validity.patternMismatch}`);

        if (validity.valid && !validity.patternMismatch) {
            console.log('✅ Username with underscores and hyphens is valid');
            testsPassed++;
        } else {
            console.log('❌ Username with underscores and hyphens incorrectly marked invalid');
            testsFailed++;
        }

        // Clear the input
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });

        // Test 7: Pattern attribute verification
        // Note: Headless Chromium doesn't reliably update validity.patternMismatch for all patterns.
        // Instead, we verify the pattern attribute exists and is correct.
        console.log('\n📋 Test 7: Pattern attribute verification');

        const patternAttr = await page.evaluate(el => el.pattern, usernameInput);
        const expectedPattern = '[a-zA-Z0-9_\\-]+';

        console.log(`   Pattern attribute: "${patternAttr}"`);
        console.log(`   Expected pattern: "${expectedPattern}"`);

        if (patternAttr === expectedPattern) {
            console.log('✅ Username input has correct pattern attribute for validation');
            testsPassed++;
        } else {
            console.log(`❌ Pattern attribute mismatch - expected "${expectedPattern}", got "${patternAttr}"`);
            testsFailed++;
        }

        // Clear the input
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });

        // Test 8: Check for any visual validation indicators
        console.log('\n📋 Test 8: Visual validation state check');
        await page.focus('input[name="username"]');
        await page.keyboard.type('test123', { delay: 50 });

        // Check for any CSS classes that might indicate invalid state
        const inputClasses = await page.evaluate(el => el.className, usernameInput);
        const hasInvalidClass = inputClasses.includes('invalid') ||
                                inputClasses.includes('is-invalid') ||
                                inputClasses.includes('error');

        // Also check computed styles for any red border or warning colors
        const computedStyle = await page.evaluate(el => {
            const style = window.getComputedStyle(el);
            return {
                borderColor: style.borderColor,
                backgroundColor: style.backgroundColor,
                boxShadow: style.boxShadow
            };
        }, usernameInput);

        console.log(`   Classes: "${inputClasses}"`);
        console.log(`   Border color: ${computedStyle.borderColor}`);

        // Check if there's a red/error border (common in validation)
        const hasErrorBorder = computedStyle.borderColor.includes('rgb(255, 0, 0)') ||
                              computedStyle.borderColor.includes('rgb(220, 53, 69)') ||
                              computedStyle.borderColor.includes('rgb(250, 92, 124)');

        if (!hasInvalidClass && !hasErrorBorder) {
            console.log('✅ No visual error state for valid input "test123"');
            testsPassed++;
        } else {
            console.log('❌ Visual error state detected for valid input');
            if (hasInvalidClass) console.log('   Has invalid class');
            if (hasErrorBorder) console.log('   Has error border color');
            testsFailed++;
        }

        // Test 9: Invalid username shows is-invalid class
        console.log('\n📋 Test 9: Invalid username shows is-invalid class');
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });
        await page.focus('input[name="username"]');
        await page.keyboard.type('@invalid!', { delay: 50 });
        // Trigger blur to activate validation
        await page.click('input[name="password"]');
        await new Promise(resolve => setTimeout(resolve, 300));

        const invalidClasses = await page.evaluate(el => el.className, usernameInput);
        console.log(`   Classes after invalid input: "${invalidClasses}"`);

        if (invalidClasses.includes('is-invalid')) {
            console.log('✅ Invalid username has is-invalid class');
            testsPassed++;
        } else {
            console.log('❌ Invalid username should have is-invalid class');
            testsFailed++;
        }

        // Test 10: Valid username shows is-valid class
        console.log('\n📋 Test 10: Valid username shows is-valid class');
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });
        await page.focus('input[name="username"]');
        await page.keyboard.type('validuser', { delay: 50 });
        // Trigger blur to activate validation
        await page.click('input[name="password"]');
        await new Promise(resolve => setTimeout(resolve, 300));

        const validClasses = await page.evaluate(el => el.className, usernameInput);
        console.log(`   Classes after valid input: "${validClasses}"`);

        if (validClasses.includes('is-valid')) {
            console.log('✅ Valid username has is-valid class');
            testsPassed++;
        } else {
            console.log('❌ Valid username should have is-valid class');
            testsFailed++;
        }

        // Test 11: Error message displays for invalid input
        console.log('\n📋 Test 11: Error message displays for invalid input');
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });
        await page.focus('input[name="username"]');
        await page.keyboard.type('@bad!', { delay: 50 });
        await page.click('input[name="password"]');
        await new Promise(resolve => setTimeout(resolve, 300));

        // Look for error message element
        const errorMessageElement = await page.$('.ldr-error-message.show, .ldr-error-message:not([style*="display: none"])');
        const usernameError = await page.$('#username-error');

        if (errorMessageElement || usernameError) {
            let errorText = '';
            if (usernameError) {
                errorText = await page.evaluate(el => el.textContent, usernameError);
            } else if (errorMessageElement) {
                errorText = await page.evaluate(el => el.textContent, errorMessageElement);
            }
            console.log(`   Error message found: "${errorText.trim().substring(0, 50)}"`);
            console.log('✅ Error message displays for invalid input');
            testsPassed++;
        } else {
            console.log('❌ Error message should display for invalid input');
            testsFailed++;
        }

        // Test 12: Error message clears on valid input
        console.log('\n📋 Test 12: Error message clears on valid input');
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });
        await page.focus('input[name="username"]');
        await page.keyboard.type('validuser', { delay: 50 });
        await page.click('input[name="password"]');
        await new Promise(resolve => setTimeout(resolve, 300));

        // Check that username error is hidden
        const usernameErrorAfter = await page.$('#username-error');
        let errorHidden = true;
        if (usernameErrorAfter) {
            const errorDisplay = await page.evaluate(el => {
                const style = window.getComputedStyle(el);
                return {
                    display: style.display,
                    visibility: style.visibility,
                    hasShowClass: el.classList.contains('show')
                };
            }, usernameErrorAfter);
            errorHidden = errorDisplay.display === 'none' || !errorDisplay.hasShowClass;
            console.log(`   Error element display: ${errorDisplay.display}, show class: ${errorDisplay.hasShowClass}`);
        }

        if (errorHidden) {
            console.log('✅ Error message clears/hides for valid input');
            testsPassed++;
        } else {
            console.log('❌ Error message should clear/hide for valid input');
            testsFailed++;
        }

        // Test 13: Password field does NOT show error while typing short password
        console.log('\n📋 Test 13: Password field does NOT show error while typing short password');
        await page.evaluate(() => { document.querySelector('input[name="password"]').value = ''; });
        await page.focus('input[name="password"]');
        await page.keyboard.type('abc', { delay: 50 });
        // Do NOT trigger blur - stay focused on the field
        await new Promise(resolve => setTimeout(resolve, 200));

        const passwordInputDuringTyping = await page.$('input[name="password"]');
        const passwordClassesDuringTyping = await page.evaluate(el => el.className, passwordInputDuringTyping);
        const strengthIndicatorVisible = await page.evaluate(() => {
            const indicator = document.querySelector('#password-strength');
            if (!indicator) return false;
            const style = window.getComputedStyle(indicator);
            return style.display !== 'none';
        });

        console.log(`   Classes while typing: "${passwordClassesDuringTyping}"`);
        console.log(`   Strength indicator visible: ${strengthIndicatorVisible}`);

        if (!passwordClassesDuringTyping.includes('is-invalid') && strengthIndicatorVisible) {
            console.log('✅ Password does NOT show error while typing, strength indicator visible');
            testsPassed++;
        } else {
            if (passwordClassesDuringTyping.includes('is-invalid')) {
                console.log('❌ Password should NOT show is-invalid class while still typing');
            }
            if (!strengthIndicatorVisible) {
                console.log('❌ Strength indicator should be visible while typing');
            }
            testsFailed++;
        }

        // Test 14: Password field shows error AFTER blur when password too short
        console.log('\n📋 Test 14: Password field shows error AFTER blur when password too short');
        await page.evaluate(() => { document.querySelector('input[name="password"]').value = ''; });
        await page.focus('input[name="password"]');
        await page.keyboard.type('short', { delay: 50 });
        // Trigger blur by clicking another field
        await page.click('input[name="confirm_password"]');
        await new Promise(resolve => setTimeout(resolve, 300));

        const passwordClassesAfterBlur = await page.evaluate(el => el.className, passwordInputDuringTyping);
        const passwordErrorVisible = await page.evaluate(() => {
            const error = document.querySelector('#password-error');
            if (!error) return false;
            const style = window.getComputedStyle(error);
            return style.display !== 'none' || error.classList.contains('show');
        });

        console.log(`   Classes after blur: "${passwordClassesAfterBlur}"`);
        console.log(`   Error message visible: ${passwordErrorVisible}`);

        if (passwordClassesAfterBlur.includes('is-invalid') && passwordErrorVisible) {
            console.log('✅ Password shows is-invalid and error message after blur');
            testsPassed++;
        } else {
            if (!passwordClassesAfterBlur.includes('is-invalid')) {
                console.log('❌ Password should have is-invalid class after blur with short password');
            }
            if (!passwordErrorVisible) {
                console.log('❌ Password error message should be visible after blur');
            }
            testsFailed++;
        }

        // Test 15: Password error clears immediately when password becomes valid
        console.log('\n📋 Test 15: Password error clears immediately when password becomes valid');
        // Start with invalid state from previous test
        await page.focus('input[name="password"]');
        // Type more characters to reach 8+ chars (already has "short" = 5 chars)
        await page.keyboard.type('123', { delay: 50 }); // Now "short123" = 8 chars
        await new Promise(resolve => setTimeout(resolve, 200));

        const passwordClassesAfterValid = await page.evaluate(el => el.className, passwordInputDuringTyping);
        console.log(`   Classes after typing valid password: "${passwordClassesAfterValid}"`);

        if (!passwordClassesAfterValid.includes('is-invalid') && passwordClassesAfterValid.includes('is-valid')) {
            console.log('✅ Password error clears immediately and shows is-valid when password becomes valid');
            testsPassed++;
        } else {
            if (passwordClassesAfterValid.includes('is-invalid')) {
                console.log('❌ is-invalid should be removed when password becomes valid');
            }
            if (!passwordClassesAfterValid.includes('is-valid')) {
                console.log('❌ is-valid should be added when password becomes valid');
            }
            testsFailed++;
        }

        // Test 16: ARIA aria-invalid attribute updates with validation state
        console.log('\n📋 Test 16: ARIA aria-invalid attribute updates with validation state');
        // Clear and type invalid username
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });
        await page.focus('input[name="username"]');
        await page.keyboard.type('@invalid!', { delay: 50 });
        await page.click('input[name="password"]');
        await new Promise(resolve => setTimeout(resolve, 300));

        const ariaInvalidTrue = await page.evaluate(() => {
            const input = document.querySelector('input[name="username"]');
            return input.getAttribute('aria-invalid');
        });
        console.log(`   aria-invalid after invalid input: "${ariaInvalidTrue}"`);

        // Now type valid username
        await page.evaluate(() => { document.querySelector('input[name="username"]').value = ''; });
        await page.focus('input[name="username"]');
        await page.keyboard.type('validuser', { delay: 50 });
        await page.click('input[name="password"]');
        await new Promise(resolve => setTimeout(resolve, 300));

        const ariaInvalidFalse = await page.evaluate(() => {
            const input = document.querySelector('input[name="username"]');
            return input.getAttribute('aria-invalid');
        });
        console.log(`   aria-invalid after valid input: "${ariaInvalidFalse}"`);

        if (ariaInvalidTrue === 'true' && ariaInvalidFalse === 'false') {
            console.log('✅ aria-invalid correctly updates based on validation state');
            testsPassed++;
        } else {
            if (ariaInvalidTrue !== 'true') {
                console.log('❌ aria-invalid should be "true" for invalid input');
            }
            if (ariaInvalidFalse !== 'false') {
                console.log('❌ aria-invalid should be "false" for valid input');
            }
            testsFailed++;
        }

        // Test 17: ARIA aria-describedby attributes are present on form inputs
        console.log('\n📋 Test 17: ARIA aria-describedby attributes are present on form inputs');
        const ariaDescribedByAttrs = await page.evaluate(() => {
            const username = document.querySelector('input[name="username"]');
            const password = document.querySelector('input[name="password"]');
            const confirmPassword = document.querySelector('input[name="confirm_password"]');
            return {
                username: username ? username.getAttribute('aria-describedby') : null,
                password: password ? password.getAttribute('aria-describedby') : null,
                confirmPassword: confirmPassword ? confirmPassword.getAttribute('aria-describedby') : null
            };
        });

        console.log(`   Username aria-describedby: "${ariaDescribedByAttrs.username}"`);
        console.log(`   Password aria-describedby: "${ariaDescribedByAttrs.password}"`);
        console.log(`   Confirm Password aria-describedby: "${ariaDescribedByAttrs.confirmPassword}"`);

        const usernameDescribedBy = ariaDescribedByAttrs.username === 'username-error';
        const passwordDescribedBy = ariaDescribedByAttrs.password === 'password-error';
        const confirmPasswordDescribedBy = ariaDescribedByAttrs.confirmPassword === 'confirm-password-error';

        if (usernameDescribedBy && passwordDescribedBy && confirmPasswordDescribedBy) {
            console.log('✅ All form inputs have correct aria-describedby attributes');
            testsPassed++;
        } else {
            if (!usernameDescribedBy) {
                console.log('❌ Username input should have aria-describedby="username-error"');
            }
            if (!passwordDescribedBy) {
                console.log('❌ Password input should have aria-describedby="password-error"');
            }
            if (!confirmPasswordDescribedBy) {
                console.log('❌ Confirm password input should have aria-describedby="confirm-password-error"');
            }
            testsFailed++;
        }

        // Test 18: Password strength indicator visible during typing (UX check)
        console.log('\n📋 Test 18: Password strength indicator visible during typing (UX check)');
        await page.evaluate(() => { document.querySelector('input[name="password"]').value = ''; });
        await page.focus('input[name="password"]');
        await page.keyboard.type('TestPass', { delay: 50 });
        await new Promise(resolve => setTimeout(resolve, 200));

        const strengthIndicatorState = await page.evaluate(() => {
            const indicator = document.querySelector('#password-strength');
            if (!indicator) return { visible: false, exists: false };
            const style = window.getComputedStyle(indicator);
            return {
                exists: true,
                visible: style.display !== 'none',
                display: style.display,
                classes: indicator.className
            };
        });

        console.log(`   Strength indicator exists: ${strengthIndicatorState.exists}`);
        console.log(`   Strength indicator visible: ${strengthIndicatorState.visible}`);
        console.log(`   Strength indicator display: ${strengthIndicatorState.display}`);
        console.log(`   Strength indicator classes: "${strengthIndicatorState.classes}"`);

        if (strengthIndicatorState.exists && strengthIndicatorState.visible) {
            console.log('✅ Password strength indicator is visible during typing');
            testsPassed++;
        } else {
            if (!strengthIndicatorState.exists) {
                console.log('❌ Password strength indicator element should exist');
            }
            if (!strengthIndicatorState.visible) {
                console.log('❌ Password strength indicator should be visible during typing');
            }
            testsFailed++;
        }

        // Take a screenshot of the final state (skip in CI)
        if (!isCI) {
            await page.screenshot({
                path: path.join(screenshotsDir, 'register_validation_test.png'),
                fullPage: true
            });
            console.log('\n📸 Screenshot saved to screenshots/register_validation_test.png');
        }

        // Summary
        console.log('\n' + '='.repeat(50));
        console.log(`📊 Test Summary: ${testsPassed} passed, ${testsFailed} failed`);
        console.log('='.repeat(50));

        if (testsFailed > 0) {
            throw new Error(`${testsFailed} test(s) failed`);
        }

        console.log('\n🎉 All registration validation tests passed!');

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

        // Take error screenshot (skip in CI)
        if (!isCI) {
            try {
                await page.screenshot({
                    path: path.join(screenshotsDir, 'register_validation_error.png'),
                    fullPage: true
                });
                console.log('📸 Error screenshot saved');
            } catch (screenshotError) {
                console.log('⚠️  Could not take error screenshot:', screenshotError.message);
            }
        }

        await browser.close();
        process.exit(1);
    }

    await browser.close();
    console.log('\n✅ Test completed successfully');
    process.exit(0);
}

// Run the test
testRegisterValidation().catch(error => {
    console.error('Fatal error:', error);
    process.exit(1);
});
