import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

import App from './App'
import { api } from './api'

vi.mock('./api', () => ({
  api: vi.fn(),
}))

vi.mock('./Uploader', () => ({
  default: function MockUploader() {
    return <div data-testid="uploader">uploader</div>
  },
}))

vi.mock('./Login', () => ({
  default: function MockLogin({ onLogin }: { onLogin: () => void }) {
    return (
      <button type="button" onClick={onLogin}>
        mock-login
      </button>
    )
  },
}))

describe('App', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.stubGlobal('location', { reload: vi.fn() } as any)
  })

  afterEach(() => {
    vi.unstubAllGlobals()
  })

  it('renders uploader when whoami is successful', async () => {
    const apiMock = vi.mocked(api)
    apiMock.mockResolvedValueOnce({ username: 'brad' } as never)

    render(<App />)

    expect(await screen.findByTestId('uploader')).toBeInTheDocument()
    expect(screen.getByRole('button', { name: 'Logout' })).toBeInTheDocument()
    expect(apiMock).toHaveBeenCalledWith('/api/whoami')
  })

  it('renders login when whoami fails', async () => {
    const apiMock = vi.mocked(api)
    apiMock.mockRejectedValueOnce(new Error('unauthorized'))

    render(<App />)

    expect(await screen.findByRole('button', { name: 'mock-login' })).toBeInTheDocument()
  })

  it('calls logout endpoint and reloads page', async () => {
    const apiMock = vi.mocked(api)
    apiMock.mockResolvedValueOnce({ username: 'brad' } as never)
    apiMock.mockResolvedValueOnce({ ok: true } as never)

    render(<App />)
    const logoutBtn = await screen.findByRole('button', { name: 'Logout' })
    fireEvent.click(logoutBtn)

    await waitFor(() => {
      expect(apiMock).toHaveBeenCalledWith('/api/logout', { method: 'POST' })
    })
    expect((globalThis.location as any).reload).toHaveBeenCalledTimes(1)
  })
})
