/**
 * Authentication Helper for UI Tests
 * Handles login and registration for Puppeteer tests
 */

const DEFAULT_TEST_USER = {
    username: 'testuser',
    password: 'T3st!Secure#2024$LDR'
};

const crypto = require('crypto');

// Generate random username for each test to avoid conflicts
function generateRandomUsername() {
    const timestamp = Date.now();
    let random;
    // Use rejection sampling to avoid bias
    const maxValue = 4294967295; // Max value for 32-bit unsigned int
    const limit = maxValue - (maxValue % 1000); // Largest multiple of 1000 that fits

    do {
        random = crypto.randomBytes(4).readUInt32BE(0);
    } while (random >= limit); // Reject values that would cause bias

    random = random % 1000;
    return `testuser_${timestamp}_${random}`;
}

class AuthHelper {
    constructor(page, baseUrl = 'http://127.0.0.1:5000') {
        this.page = page;
        this.baseUrl = baseUrl;
    }

    /**
     * Check if user is logged in by looking for logout button or username
     */
    async isLoggedIn() {
        try {
            // Check if we're on a page that requires auth
            const url = this.page.url();
            console.log('Checking login status at URL:', url);

            if (url.includes('/auth/login')) {
                console.log('On login page - not logged in');
                return false;
            }

            // Check for logout button/link
            const logoutSelectors = [
                'a.logout-btn',
                '#logout-form',
                'form[action="/auth/logout"]',
                'a[onclick*="logout"]'
            ];

            for (const selector of logoutSelectors) {
                try {
                    const element = await this.page.$(selector);
                    if (element) {
                        console.log(`Found logout element with selector: ${selector}`);
                        return true;
                    }
                } catch (e) {
                    // Some selectors might not be valid, continue
                }
            }

            // Check if we can access protected pages
            const currentUrl = this.page.url();
            if (currentUrl.includes('/settings') || currentUrl.includes('/metrics') || currentUrl.includes('/history')) {
                console.log('On protected page - logged in');
                return true;
            }

            // If we're on the home page, check for research form
            const researchForm = await this.page.$('form[action*="research"], #query, button[type="submit"]');
            if (researchForm) {
                console.log('Found research form - likely logged in');
                return true;
            }

            console.log('No login indicators found');
            return false;
        } catch (error) {
            console.log('Error checking login status:', error.message);
            return false;
        }
    }

    /**
     * Login with existing user credentials
     */
    async login(username = DEFAULT_TEST_USER.username, password = DEFAULT_TEST_USER.password) {
        console.log(`🔐 Attempting login as ${username}...`);

        // Check if already logged in
        if (await this.isLoggedIn()) {
            console.log('✅ Already logged in');
            return true;
        }

        // Navigate to login page only if not already there
        const currentUrl = this.page.url();
        if (!currentUrl.includes('/auth/login')) {
            await this.page.goto(`${this.baseUrl}/auth/login`, {
                waitUntil: 'networkidle2',
                timeout: 30000
            });
        }

        // Wait for login form
        await this.page.waitForSelector('input[name="username"]', { timeout: 5000 });

        // Fill in credentials
        await this.page.type('input[name="username"]', username);
        await this.page.type('input[name="password"]', password);

        // Submit form
        await Promise.all([
            this.page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 60000 }), // 60 second timeout for slow database creation
            this.page.click('button[type="submit"]')
        ]);

        // Check if login was successful
        const finalUrl = this.page.url();
        if (finalUrl.includes('/auth/login')) {
            // Still on login page - check for error
            const error = await this.page.$('.alert-danger, .error-message');
            if (error) {
                const errorText = await this.page.evaluate(el => el.textContent, error);
                throw new Error(`Login failed: ${errorText}`);
            }
            throw new Error('Login failed - still on login page');
        }

        console.log('✅ Login successful');
        return true;
    }

    /**
     * Register a new user
     */
    async register(username = DEFAULT_TEST_USER.username, password = DEFAULT_TEST_USER.password) {
        console.log(`📝 Attempting registration for ${username}...`);

        // Navigate to registration page
        await this.page.goto(`${this.baseUrl}/auth/register`, {
            waitUntil: 'networkidle2',
            timeout: 30000
        });

        // Wait for registration form
        await this.page.waitForSelector('input[name="username"]', { timeout: 5000 });

        // Fill in registration form
        await this.page.type('input[name="username"]', username);
        await this.page.type('input[name="password"]', password);
        await this.page.type('input[name="confirm_password"]', password);

        // Check acknowledgment checkbox if present
        const acknowledgeCheckbox = await this.page.$('input[name="acknowledge"]');
        if (acknowledgeCheckbox) {
            await this.page.click('input[name="acknowledge"]');
        }

        // Submit form
        await Promise.all([
            this.page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 90000 }), // 90 second timeout for slow database creation in CI
            this.page.click('button[type="submit"]')
        ]);

        // Check if registration was successful
        const currentUrl = this.page.url();
        if (currentUrl.includes('/auth/register')) {
            // Still on registration page - check for actual errors (not warnings)
            const error = await this.page.$('.alert-danger:not(.alert-warning), .error-message');
            if (error) {
                const errorText = await this.page.evaluate(el => el.textContent, error);
                if (errorText.includes('already exists')) {
                    console.log('⚠️  User already exists, attempting login instead');
                    return await this.login(username, password);
                }
                throw new Error(`Registration failed: ${errorText}`);
            }

            // Check for security warnings (these are not errors)
            const warning = await this.page.$('.alert-warning');
            if (warning) {
                const warningText = await this.page.evaluate(el => el.textContent, warning);
                console.log('⚠️  Security warning:', warningText.trim().replace(/\s+/g, ' '));
            }

            throw new Error('Registration failed - still on registration page');
        }

        console.log('✅ Registration successful');
        return true;
    }

    /**
     * Ensure user is authenticated - register if needed, then login
     */
    async ensureAuthenticated(username = null, password = DEFAULT_TEST_USER.password, retries = 2) {
        // Generate random username if not provided
        if (!username) {
            username = generateRandomUsername();
            console.log(`🎲 Using random username: ${username}`);
        }

        // Check if already logged in
        if (await this.isLoggedIn()) {
            console.log('✅ Already logged in');
            return true;
        }

        let lastError;
        for (let attempt = 1; attempt <= retries; attempt++) {
            try {
                // Try to login first
                return await this.login(username, password);
            } catch (loginError) {
                console.log(`⚠️  Login failed (attempt ${attempt}/${retries}), attempting registration...`);
                try {
                    // If login fails, try to register
                    return await this.register(username, password);
                } catch (registerError) {
                    lastError = registerError;
                    // If registration also fails (user exists), try login again
                    if (registerError.message.includes('already exists')) {
                        try {
                            return await this.login(username, password);
                        } catch (secondLoginError) {
                            lastError = secondLoginError;
                        }
                    }

                    if (attempt < retries && registerError.message.includes('timeout')) {
                        console.log(`⚠️  Timeout error, retrying (${attempt}/${retries})...`);
                        await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds before retry
                        continue;
                    }

                    if (attempt === retries) {
                        throw registerError;
                    }
                }
            }
        }

        throw lastError || new Error('Failed to authenticate after retries');
    }

    /**
     * Logout the current user
     */
    async logout() {
        console.log('🚪 Logging out...');

        try {
            // Look for logout link/button
            const logoutLink = await this.page.$('a.logout-btn');
            if (logoutLink) {
                console.log('  Found logout link, clicking...');
                await Promise.all([
                    this.page.waitForNavigation({
                        waitUntil: 'networkidle2',
                        timeout: 10000
                    }).catch(err => {
                        console.log('  Navigation wait timed out, checking URL...');
                    }),
                    this.page.click('a.logout-btn')
                ]);
            } else {
                // Try to find and submit the logout form
                const logoutForm = await this.page.$('#logout-form');
                if (logoutForm) {
                    console.log('  Found logout form, submitting...');
                    await Promise.all([
                        this.page.waitForNavigation({
                            waitUntil: 'networkidle2',
                            timeout: 10000
                        }).catch(err => {
                            console.log('  Navigation wait timed out, checking URL...');
                        }),
                        this.page.evaluate(() => {
                            document.getElementById('logout-form').submit();
                        })
                    ]);
                } else {
                    // Fallback: navigate directly to logout URL
                    console.log('  No logout button/form found, navigating directly to /auth/logout...');
                    await this.page.goto(`${this.page.url().split('/').slice(0, 3).join('/')}/auth/logout`, {
                        waitUntil: 'networkidle2',
                        timeout: 10000
                    });
                }
            }

            // Give it a moment for any redirects
            await this.page.waitForTimeout(1000);

            // Ensure we're on the login page or logged out
            const currentUrl = this.page.url();
            console.log(`  Current URL after logout: ${currentUrl}`);

            // Check if we're logged out by looking for login form
            const loginForm = await this.page.$('form[action*="login"], input[name="username"]');
            if (loginForm || currentUrl.includes('/auth/login')) {
                console.log('✅ Logged out successfully');
            } else {
                // Double-check by trying to access a protected page
                await this.page.goto(`${this.page.url().split('/').slice(0, 3).join('/')}/settings/`, {
                    waitUntil: 'networkidle2',
                    timeout: 5000
                }).catch(() => {});

                const finalUrl = this.page.url();
                if (finalUrl.includes('/auth/login')) {
                    console.log('✅ Logged out successfully (verified via protected page)');
                } else {
                    console.log(`Warning: May not be fully logged out. Current URL: ${finalUrl}`);
                }
            }
        } catch (error) {
            console.log(`⚠️ Logout error: ${error.message}`);
            // Continue anyway - we'll verify logout status
        }
    }
}

module.exports = AuthHelper;
