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

import type { TokenBundle } from '@/types'
import { AuthContext, type AuthContextValue } from '@/app/providers/auth-context'
import { OAuthCallbackPage } from './OAuthCallbackPage'

const exchangeOAuthHandoffMock = vi.fn<(code: string) => Promise<TokenBundle>>()

vi.mock('@/services/auth', () => ({
  exchangeOAuthHandoff: (code: string) => exchangeOAuthHandoffMock(code),
}))

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

function renderOAuthCallbackPage(entry: string) {
  return render(
    <MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={[entry]}>
      <AuthContext.Provider value={authContextValue}>
        <OAuthCallbackPage />
      </AuthContext.Provider>
    </MemoryRouter>,
  )
}

describe('OAuthCallbackPage', () => {
  beforeEach(() => {
    exchangeOAuthHandoffMock.mockReset()
    vi.mocked(authContextValue.onLoginSuccess).mockClear()
  })

  it('exchanges handoff code and completes login', async () => {
    exchangeOAuthHandoffMock.mockResolvedValue({
      access_token: 'access-token',
      refresh_token: 'refresh-token',
      expires_in: 7200,
      user: {
        id: 1,
        username: 'oauth_user',
        email: 'oauth@example.com',
        phone: '',
        nickname: 'OAuth User',
        avatar: '',
        status: 1,
      },
    })

    renderOAuthCallbackPage('/login/oauth/callback?redirect=%2Fusers#status=success&code=handoff-code&provider=github')

    await waitFor(() => expect(exchangeOAuthHandoffMock).toHaveBeenCalledWith('handoff-code'))
    await waitFor(() => expect(authContextValue.onLoginSuccess).toHaveBeenCalledTimes(1))
  })

  it('shows error state returned from oauth callback fragment', async () => {
    renderOAuthCallbackPage('/login/oauth/callback#status=error&message=OAuth%20Denied')

    expect(await screen.findByText('第三方登录失败')).toBeInTheDocument()
    expect(screen.getByText('OAuth Denied')).toBeInTheDocument()
  })
})
