import { render, screen } from '@testing-library/react'
import { describe, expect, it } from 'vitest'
import { MemoryRouter, Route, Routes, useLocation } from 'react-router-dom'
import type { ReactNode } from 'react'

import { AuthContext, type AuthContextValue } from '@/app/providers/auth-context'
import { RequireAdmin } from './RequireAdmin'
import { RequireAuth } from './RequireAuth'

const baseAuthContextValue: AuthContextValue = {
  user: null,
  roles: [],
  isAdmin: false,
  isAuthenticated: false,
  isLoading: false,
  onLoginSuccess: async () => {},
  logout: async () => {},
  refreshUser: async () => {},
}

function LocationProbe() {
  const location = useLocation()
  const fromPath =
    (location.state as { from?: { pathname?: string } } | null)?.from?.pathname ?? 'none'

  return (
    <div>
      <span data-testid="pathname">{location.pathname}</span>
      <span data-testid="from-path">{fromPath}</span>
    </div>
  )
}

function renderWithAuth(
  authContextValue: Partial<AuthContextValue>,
  router: ReactNode,
) {
  const value: AuthContextValue = {
    ...baseAuthContextValue,
    ...authContextValue,
  }

  return render(
    <AuthContext.Provider value={value}>
      {router}
    </AuthContext.Provider>,
  )
}

describe('RequireAuth', () => {
  it('shows a loading indicator while auth state is being restored', () => {
    const { container } = renderWithAuth(
      { isLoading: true },
      <MemoryRouter initialEntries={['/users']}>
        <Routes>
          <Route
            path="/users"
            element={(
              <RequireAuth>
                <div>private content</div>
              </RequireAuth>
            )}
          />
        </Routes>
      </MemoryRouter>,
    )

    expect(container.querySelector('[aria-busy="true"]')).toBeInTheDocument()
    expect(screen.queryByText('private content')).not.toBeInTheDocument()
  })

  it('redirects unauthenticated users to login and preserves the original route', async () => {
    renderWithAuth(
      { isAuthenticated: false, isLoading: false },
      <MemoryRouter initialEntries={['/users']}>
        <Routes>
          <Route
            path="/users"
            element={(
              <RequireAuth>
                <div>private content</div>
              </RequireAuth>
            )}
          />
          <Route path="/login" element={<LocationProbe />} />
        </Routes>
      </MemoryRouter>,
    )

    expect(await screen.findByTestId('pathname')).toHaveTextContent('/login')
    expect(screen.getByTestId('from-path')).toHaveTextContent('/users')
  })

  it('renders protected content when authenticated', () => {
    renderWithAuth(
      {
        isAuthenticated: true,
        isLoading: false,
        user: {
          id: 1,
          username: 'admin',
          email: 'admin@example.com',
          phone: '',
          nickname: 'Admin',
          avatar: '',
          status: 1,
        },
      },
      <MemoryRouter initialEntries={['/users']}>
        <Routes>
          <Route
            path="/users"
            element={(
              <RequireAuth>
                <div>private content</div>
              </RequireAuth>
            )}
          />
        </Routes>
      </MemoryRouter>,
    )

    expect(screen.getByText('private content')).toBeInTheDocument()
  })
})

describe('RequireAdmin', () => {
  it('waits silently while auth state is still loading', () => {
    const { container } = renderWithAuth(
      { isLoading: true, isAdmin: false },
      <MemoryRouter initialEntries={['/dashboard']}>
        <Routes>
          <Route
            path="/dashboard"
            element={(
              <RequireAdmin>
                <div>admin dashboard</div>
              </RequireAdmin>
            )}
          />
        </Routes>
      </MemoryRouter>,
    )

    expect(container).toBeEmptyDOMElement()
  })

  it('redirects non-admin users to profile', async () => {
    renderWithAuth(
      { isLoading: false, isAdmin: false, isAuthenticated: true },
      <MemoryRouter initialEntries={['/dashboard']}>
        <Routes>
          <Route
            path="/dashboard"
            element={(
              <RequireAdmin>
                <div>admin dashboard</div>
              </RequireAdmin>
            )}
          />
          <Route path="/profile" element={<LocationProbe />} />
        </Routes>
      </MemoryRouter>,
    )

    expect(await screen.findByTestId('pathname')).toHaveTextContent('/profile')
  })

  it('renders admin-only content for admins', () => {
    renderWithAuth(
      { isLoading: false, isAdmin: true, isAuthenticated: true },
      <MemoryRouter initialEntries={['/dashboard']}>
        <Routes>
          <Route
            path="/dashboard"
            element={(
              <RequireAdmin>
                <div>admin dashboard</div>
              </RequireAdmin>
            )}
          />
        </Routes>
      </MemoryRouter>,
    )

    expect(screen.getByText('admin dashboard')).toBeInTheDocument()
  })
})
