/**
 * OAuth No-Browser Mode Unit Tests
 *
 * Tests the extractCodeFromInput() function which enables OAuth authentication
 * on headless servers without a desktop environment.
 *
 * ============================================================================
 * FEATURE: --no-browser OAuth Mode
 * ============================================================================
 *
 * PURPOSE:
 *   Allow users to add Google accounts on remote servers (headless Linux,
 *   Docker containers, SSH sessions) where automatic browser opening is
 *   not possible.
 *
 * USAGE:
 *   npm run accounts:add -- --no-browser
 *
 * USER FLOW:
 *   1. User runs command on headless server
 *   2. System displays Google OAuth URL
 *   3. User opens URL on another device (phone/laptop) with a browser
 *   4. User signs in to Google and authorizes the app
 *   5. Browser redirects to localhost (page won't load - this is expected)
 *   6. User copies the redirect URL or authorization code from address bar
 *   7. User pastes into server terminal
 *   8. System extracts code using extractCodeFromInput() (tested here)
 *   9. Account is added successfully
 *
 * FUNCTION UNDER TEST:
 *   extractCodeFromInput(input: string) => { code: string, state: string|null }
 *
 *   Accepts either:
 *   - Full callback URL: http://localhost:51121/callback?code=xxx&state=yyy
 *   - Raw authorization code: 4/0AQSTgQG...
 *
 *   Throws on:
 *   - Empty/null input
 *   - Too short input (< 10 chars)
 *   - URL with OAuth error parameter
 *   - URL without code parameter
 *
 * ============================================================================
 *
 * Run: node tests/test-oauth-no-browser.cjs
 */

// Note: Using dynamic import because oauth.js is ESM
async function runTests() {
    console.log('='.repeat(60));
    console.log('OAUTH NO-BROWSER MODE UNIT TESTS');
    console.log('Testing: extractCodeFromInput()');
    console.log('='.repeat(60));
    console.log('');

    // Import the ESM module
    const { extractCodeFromInput } = await import('../src/auth/oauth.js');

    let allPassed = true;
    const results = [];

    /**
     * Helper to run a single test case
     * @param {string} name - Test name
     * @param {Function} testFn - Test function that returns { passed, message }
     */
    async function test(name, testFn) {
        try {
            const { passed, message } = await testFn();
            results.push({ name, passed, message });
            const status = passed ? 'PASS' : 'FAIL';
            console.log(`  [${status}] ${name}`);
            if (message) console.log(`         ${message}`);
            if (!passed) allPassed = false;
        } catch (error) {
            results.push({ name, passed: false, message: error.message });
            console.log(`  [FAIL] ${name}`);
            console.log(`         Error: ${error.message}`);
            allPassed = false;
        }
    }

    // ===== Test Group 1: Valid URL Inputs =====
    console.log('\n--- Valid URL Inputs ---');

    await test('Parse full callback URL with code and state', () => {
        const input = 'http://localhost:51121/oauth-callback?code=4/0AQSTg123&state=abc123';
        const result = extractCodeFromInput(input);
        const passed = result.code === '4/0AQSTg123' && result.state === 'abc123';
        return { passed, message: `code=${result.code}, state=${result.state}` };
    });

    await test('Parse URL with only code (no state)', () => {
        const input = 'http://localhost:51121/oauth-callback?code=4/0AQSTg456';
        const result = extractCodeFromInput(input);
        const passed = result.code === '4/0AQSTg456' && result.state === null;
        return { passed, message: `code=${result.code}, state=${result.state}` };
    });

    await test('Parse HTTPS URL', () => {
        const input = 'https://localhost:51121/callback?code=secureCode123&state=xyz';
        const result = extractCodeFromInput(input);
        const passed = result.code === 'secureCode123';
        return { passed, message: `code=${result.code}` };
    });

    await test('Parse URL with additional query params', () => {
        const input = 'http://localhost:51121/?code=myCode&state=myState&scope=email';
        const result = extractCodeFromInput(input);
        const passed = result.code === 'myCode' && result.state === 'myState';
        return { passed, message: `code=${result.code}, state=${result.state}` };
    });

    // ===== Test Group 2: Raw Code Inputs =====
    console.log('\n--- Raw Authorization Code Inputs ---');

    await test('Parse raw authorization code (Google format)', () => {
        const input = '4/0AQSTgQGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
        const result = extractCodeFromInput(input);
        const passed = result.code === input && result.state === null;
        return { passed, message: `code length=${result.code.length}` };
    });

    await test('Parse raw code with whitespace (should trim)', () => {
        const input = '  4/0AQSTgQGcode123  \n';
        const result = extractCodeFromInput(input);
        const passed = result.code === '4/0AQSTgQGcode123' && result.state === null;
        return { passed, message: `trimmed code=${result.code}` };
    });

    // ===== Test Group 3: Error Cases =====
    console.log('\n--- Error Handling ---');

    await test('Throw on empty input', () => {
        try {
            extractCodeFromInput('');
            return { passed: false, message: 'Should have thrown' };
        } catch (e) {
            return { passed: e.message.includes('No input'), message: e.message };
        }
    });

    await test('Throw on null input', () => {
        try {
            extractCodeFromInput(null);
            return { passed: false, message: 'Should have thrown' };
        } catch (e) {
            return { passed: e.message.includes('No input'), message: e.message };
        }
    });

    await test('Throw on too short code', () => {
        try {
            extractCodeFromInput('abc');
            return { passed: false, message: 'Should have thrown' };
        } catch (e) {
            return { passed: e.message.includes('too short'), message: e.message };
        }
    });

    await test('Throw on OAuth error in URL', () => {
        try {
            const input = 'http://localhost:51121/?error=access_denied&error_description=User%20denied';
            extractCodeFromInput(input);
            return { passed: false, message: 'Should have thrown' };
        } catch (e) {
            return { passed: e.message.includes('OAuth error'), message: e.message };
        }
    });

    await test('Throw on URL without code param', () => {
        try {
            extractCodeFromInput('http://localhost:51121/callback?state=onlyState');
            return { passed: false, message: 'Should have thrown' };
        } catch (e) {
            return { passed: e.message.includes('No authorization code'), message: e.message };
        }
    });

    // ===== Test Group 4: Edge Cases =====
    console.log('\n--- Edge Cases ---');

    await test('Handle URL-encoded characters in code', () => {
        const input = 'http://localhost:51121/?code=4%2F0AQSTg%2B%2B&state=test';
        const result = extractCodeFromInput(input);
        // URL class automatically decodes
        const passed = result.code === '4/0AQSTg++';
        return { passed, message: `decoded code=${result.code}` };
    });

    await test('Accept minimum valid code length (10 chars)', () => {
        const input = '1234567890';
        const result = extractCodeFromInput(input);
        const passed = result.code === input;
        return { passed, message: `code=${result.code}` };
    });

    // ===== Summary =====
    console.log('\n' + '='.repeat(60));
    console.log('SUMMARY');
    console.log('='.repeat(60));

    const passed = results.filter(r => r.passed).length;
    const failed = results.filter(r => !r.passed).length;
    console.log(`  Total: ${results.length} | Passed: ${passed} | Failed: ${failed}`);

    console.log('\n' + '='.repeat(60));
    console.log(allPassed ? 'ALL TESTS PASSED' : 'SOME TESTS FAILED');
    console.log('='.repeat(60));

    process.exit(allPassed ? 0 : 1);
}

runTests().catch(err => {
    console.error('Test suite failed:', err);
    process.exit(1);
});
