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

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

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

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

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

describe('App', () => {
  beforeEach(() => {
    jest.clearAllMocks()
  })

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

    render(<App />)

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

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

    render(<App />)

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

  it('switches to uploader after login callback', async () => {
    const apiMock = jest.mocked(api)
    apiMock.mockRejectedValueOnce(new Error('unauthorized'))

    render(<App />)

    const loginBtn = await screen.findByRole('button', { name: 'mock-login' })
    fireEvent.click(loginBtn)

    expect(await screen.findByTestId('uploader')).toBeTruthy()
  })

  it('calls logout endpoint and reloads page', async () => {
    const apiMock = jest.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' })
    })
  })
})
