import { describe, it, expect, beforeEach } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import { Routes, Route, MemoryRouter } from 'react-router-dom'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { http, HttpResponse } from 'msw'
import { server } from '@/test/mocks/server'
import { mockUser, mockDakUser } from '@/test/mocks/data'
import { AuthProvider } from '@/context/AuthContext'
import { ProtectedRoute } from '@/components/layout/ProtectedRoute'

/**
 * Helper: renders a mini app with Routes so we can detect redirects.
 * ProtectedRoute uses <Navigate to="/login" replace />, so we set up
 * a /login route that renders identifiable text.
 */
function renderWithRoutes(
  options: {
    initialRoute?: string
    requireAdmin?: boolean
  } = {},
) {
  const { initialRoute = '/protected', requireAdmin = false } = options
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false, gcTime: 0 },
      mutations: { retry: false },
    },
  })

  return render(
    <QueryClientProvider client={queryClient}>
      <MemoryRouter initialEntries={[initialRoute]}>
        <AuthProvider>
          <Routes>
            <Route
              path="/protected"
              element={
                <ProtectedRoute requireAdmin={requireAdmin}>
                  <div>Protected Content</div>
                </ProtectedRoute>
              }
            />
            <Route path="/login" element={<div>Login Page</div>} />
          </Routes>
        </AuthProvider>
      </MemoryRouter>
    </QueryClientProvider>,
  )
}

describe('ProtectedRoute', () => {
  beforeEach(() => {
    localStorage.removeItem('access_token')
    localStorage.removeItem('refresh_token')
  })

  it('redirects to /login when not authenticated (no token)', async () => {
    // No token in localStorage -> AuthProvider sets isLoading=false, user=null immediately
    // ProtectedRoute sees !user -> <Navigate to="/login" />
    server.use(
      http.get('/api/auth/me', () => {
        return new HttpResponse(null, { status: 401 })
      }),
    )

    renderWithRoutes()

    await waitFor(() => {
      expect(screen.getByText('Login Page')).toBeInTheDocument()
    })
    expect(screen.queryByText('Protected Content')).not.toBeInTheDocument()
  })

  it('redirects to /login when token exists but /auth/me returns 401', async () => {
    // Token is set but API rejects it (expired, invalid)
    localStorage.setItem('access_token', 'expired-token')

    server.use(
      http.get('/api/auth/me', () => {
        return new HttpResponse(null, { status: 401 })
      }),
    )

    renderWithRoutes()

    await waitFor(() => {
      expect(screen.getByText('Login Page')).toBeInTheDocument()
    })
    expect(screen.queryByText('Protected Content')).not.toBeInTheDocument()
  })

  it('shows children when authenticated as admin', async () => {
    localStorage.setItem('access_token', 'valid-token')

    // Default handler returns mockUser (admin) — no override needed

    renderWithRoutes()

    await waitFor(() => {
      expect(screen.getByText('Protected Content')).toBeInTheDocument()
    })
    expect(screen.queryByText('Login Page')).not.toBeInTheDocument()
  })

  it('shows children when authenticated as dak_mitarbeiter (no requireAdmin)', async () => {
    localStorage.setItem('access_token', 'valid-token')

    server.use(
      http.get('/api/auth/me', () => {
        return HttpResponse.json(mockDakUser)
      }),
    )

    renderWithRoutes({ requireAdmin: false })

    await waitFor(() => {
      expect(screen.getByText('Protected Content')).toBeInTheDocument()
    })
    expect(screen.queryByText('Login Page')).not.toBeInTheDocument()
  })

  it('blocks non-admin users when requireAdmin is true', async () => {
    localStorage.setItem('access_token', 'valid-token')

    server.use(
      http.get('/api/auth/me', () => {
        return HttpResponse.json(mockDakUser)
      }),
    )

    renderWithRoutes({ requireAdmin: true })

    await waitFor(() => {
      expect(screen.getByText('Zugriff verweigert')).toBeInTheDocument()
    })
    expect(screen.queryByText('Protected Content')).not.toBeInTheDocument()
    expect(screen.queryByText('Login Page')).not.toBeInTheDocument()
  })

  it('allows admin users when requireAdmin is true', async () => {
    localStorage.setItem('access_token', 'valid-token')

    // Default handler returns mockUser (role: 'admin')

    renderWithRoutes({ requireAdmin: true })

    await waitFor(() => {
      expect(screen.getByText('Protected Content')).toBeInTheDocument()
    })
    expect(screen.queryByText('Zugriff verweigert')).not.toBeInTheDocument()
  })

  it('shows loading state while auth is checking', async () => {
    localStorage.setItem('access_token', 'valid-token')

    // Delay the /auth/me response to observe the loading state
    server.use(
      http.get('/api/auth/me', async () => {
        await new Promise((resolve) => setTimeout(resolve, 500))
        return HttpResponse.json(mockUser)
      }),
    )

    renderWithRoutes()

    // ProtectedRoute shows "Laden..." while isLoading is true
    expect(screen.getByText('Laden...')).toBeInTheDocument()
    expect(screen.queryByText('Protected Content')).not.toBeInTheDocument()
    expect(screen.queryByText('Login Page')).not.toBeInTheDocument()

    // After the delayed response resolves, the protected content should appear
    await waitFor(() => {
      expect(screen.getByText('Protected Content')).toBeInTheDocument()
    })
    expect(screen.queryByText('Laden...')).not.toBeInTheDocument()
  })
})
