import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { AuthProvider, useAuth } from './AuthContext'

const navigateMock = vi.fn()
const setTokenMock = vi.fn()
const logoutMock = vi.fn().mockResolvedValue(undefined)

vi.mock('react-router-dom', async () => {
  const actual = await vi.importActual<typeof import('react-router-dom')>('react-router-dom')
  return {
    ...actual,
    useNavigate: () => navigateMock
  }
})

vi.mock('../services/apiClient', () => ({
  apiClient: {
    setToken: setTokenMock
  }
}))

vi.mock('../services/auth', () => ({
  authService: {
    logout: logoutMock
  }
}))

function AuthConsumer() {
  const { user, loading, login, logout, setUser } = useAuth()
  return (
    <div>
      <div data-testid="loading">{loading ? 'loading' : 'ready'}</div>
      <div data-testid="username">{user?.name ?? 'guest'}</div>
      <button onClick={() => login('token-123', 'admin', 'Admin User', '1')}>login admin</button>
      <button onClick={() => login('token-456', 'delivery', 'Delivery User', '2')}>login delivery</button>
      <button
        onClick={() =>
          setUser({
            token: 'token-999',
            role: 'seller',
            name: 'Seller User',
            id: '3',
            companyId: 'company-1'
          })
        }
      >
        set user
      </button>
      <button onClick={logout}>logout</button>
    </div>
  )
}

describe('AuthProvider', () => {
  beforeEach(() => {
    localStorage.clear()
    navigateMock.mockReset()
    setTokenMock.mockReset()
    logoutMock.mockClear()
  })

  afterEach(() => {
    localStorage.clear()
  })

  it('hydrates user from localStorage and sets token', async () => {
    const persisted = { token: 'persisted-token', role: 'admin', name: 'Persisted', id: '10' }
    localStorage.setItem('mp-auth-user', JSON.stringify(persisted))

    render(
      <AuthProvider>
        <AuthConsumer />
      </AuthProvider>
    )

    expect(screen.getByTestId('username')).toHaveTextContent('Persisted')
    expect(setTokenMock).toHaveBeenCalledWith('persisted-token')
    await waitFor(() => expect(screen.getByTestId('loading')).toHaveTextContent('ready'))
  })

  it('logs in and navigates based on role', async () => {
    const user = userEvent.setup()
    render(
      <AuthProvider>
        <AuthConsumer />
      </AuthProvider>
    )

    await user.click(screen.getByRole('button', { name: /login admin/i }))

    expect(navigateMock).toHaveBeenCalledWith('/dashboard', { replace: true })
    expect(localStorage.getItem('mp-auth-user')).toContain('Admin User')

    await user.click(screen.getByRole('button', { name: /login delivery/i }))
    expect(navigateMock).toHaveBeenCalledWith('/entregas', { replace: true })
  })

  it('clears session on logout', async () => {
    const user = userEvent.setup()
    render(
      <AuthProvider>
        <AuthConsumer />
      </AuthProvider>
    )

    await user.click(screen.getByRole('button', { name: /set user/i }))
    expect(localStorage.getItem('mp-auth-user')).toContain('Seller User')

    await user.click(screen.getByRole('button', { name: /logout/i }))

    expect(logoutMock).toHaveBeenCalledTimes(1)
    await waitFor(() => expect(localStorage.getItem('mp-auth-user'))).toBeNull()
    expect(navigateMock).toHaveBeenCalledWith('/login', { replace: true })
    expect(setTokenMock).toHaveBeenCalledWith(null)
  })

  it('throws when useAuth is used outside provider', () => {
    const ConsoleError = console.error
    console.error = vi.fn()
    expect(() => render(<AuthConsumer />)).toThrow('useAuth must be used within AuthProvider')
    console.error = ConsoleError
  })
})
