/**
 * Integration tests for JWT Context
 *
 * These tests call the REAL backend - no mocks.
 * Backend must be running: docker-compose up
 *
 * Run with: RUN_INTEGRATION_TESTS=true npm run test
 *
 * Note: Most JWT operations require an authenticated session.
 * Tests that require authentication verify 401 handling (expected for anonymous users).
 */

import { renderHook, act, waitFor } from '@testing-library/react'
import { ReactNode } from 'react'
import { JWTContext, useJWT, useJWTRequired } from '../JWTContext'
import { describeIntegration, BACKEND_URL } from '../../testing'

function createWrapper(config?: Parameters<typeof JWTContext>[0]['config']) {
    return function Wrapper({ children }: { children: ReactNode }) {
        return (
            <JWTContext config={{
                autoObtain: false,
                baseUrl: BACKEND_URL,
                ...config
            }}>
                {children}
            </JWTContext>
        )
    }
}

describeIntegration('JWTContext (integration)', () => {
    describe('Hook behavior outside provider', () => {
        it('should return null when useJWT used outside JWTContext', () => {
            const { result } = renderHook(() => useJWT())
            expect(result.current).toBeNull()
        })

        it('should throw when useJWTRequired used outside JWTContext', () => {
            // Suppress console.error for this test
            const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {})

            expect(() => {
                renderHook(() => useJWTRequired())
            }).toThrow('useJWTRequired must be used within JWTContext')

            consoleSpy.mockRestore()
        })
    })

    describe('Token operations with real backend', () => {
        it('should handle 401 when obtaining tokens as anonymous user', async () => {
            const { result } = renderHook(() => useJWTRequired(), {
                wrapper: createWrapper(),
            })

            await act(async () => {
                const tokens = await result.current.obtainTokens()
                // Anonymous users get 401, which returns null (not an error)
                expect(tokens).toBeNull()
            })

            expect(result.current.tokens).toBeNull()
            // 401 is graceful - not an error state
            expect(result.current.error).toBeNull()
        })

        it('should handle 401 when refreshing tokens without valid refresh token', async () => {
            const { result } = renderHook(() => useJWTRequired(), {
                wrapper: createWrapper(),
            })

            // Try to refresh without any tokens - should fail gracefully
            await act(async () => {
                const tokens = await result.current.refreshTokens()
                expect(tokens).toBeNull()
            })

            expect(result.current.tokens).toBeNull()
        })
    })

    describe('clearTokens (no backend needed)', () => {
        it('should be safe to call clearTokens when no tokens are set', async () => {
            // Verifies clearTokens doesn't throw or corrupt state when called
            // with no tokens present (e.g., during logout when already logged out)
            const { result } = renderHook(() => useJWTRequired(), {
                wrapper: createWrapper(),
            })

            act(() => {
                result.current.clearTokens()
            })

            expect(result.current.tokens).toBeNull()
            expect(result.current.error).toBeNull()
        })
    })

    describe('getAccessToken', () => {
        it('should return null when no tokens available', async () => {
            const { result } = renderHook(() => useJWTRequired(), {
                wrapper: createWrapper(),
            })

            let token: string | null = 'not-null'
            await act(async () => {
                token = await result.current.getAccessToken()
            })

            expect(token).toBeNull()
        })
    })

    describe('autoObtain with real backend', () => {
        it('should attempt auto-obtain and handle 401 for anonymous', async () => {
            const { result } = renderHook(() => useJWTRequired(), {
                wrapper: createWrapper({ autoObtain: true }),
            })

            // Auto-obtain will attempt to get tokens but fail for anonymous user
            await waitFor(() => {
                // After auto-obtain completes, tokens should be null (401 response)
                // or the loading state should be done
                expect(result.current.isLoading).toBe(false)
            }, { timeout: 5000 })

            // Anonymous user won't have tokens
            expect(result.current.tokens).toBeNull()
        })
    })
})

/**
 * Note on authenticated JWT tests:
 *
 * To fully test JWT token obtain/refresh, we would need:
 * 1. An authenticated user session (login first)
 * 2. Valid CSRF token handling
 *
 * These scenarios are better tested in E2E tests (Playwright/Cypress)
 * where we can:
 * 1. Navigate to login page
 * 2. Submit credentials
 * 3. Then test JWT token flows
 *
 * The tests above verify:
 * - Hook API contract (throws/returns null outside provider)
 * - Graceful 401 handling (anonymous users)
 * - State management (clearTokens)
 * - Integration with real backend (network calls happen)
 */
