import { MemoryRouter, Route, Routes } from 'react-router-dom'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { message } from 'antd'
import { beforeEach, describe, expect, it, vi } from 'vitest'

import type { AuthCapabilities } from '@/types'
import { ForgotPasswordPage } from './ForgotPasswordPage'

const getAuthCapabilitiesMock = vi.fn<() => Promise<AuthCapabilities>>()
const forgotPasswordMock = vi.fn<(payload: { email: string }) => Promise<void>>()

vi.mock('@/services/auth', () => ({
  getAuthCapabilities: () => getAuthCapabilitiesMock(),
  forgotPassword: (payload: { email: string }) => forgotPasswordMock(payload),
}))

function renderForgotPasswordPage() {
  return render(
    <MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={['/forgot-password']}>
      <Routes>
        <Route path="/forgot-password" element={<ForgotPasswordPage />} />
      </Routes>
    </MemoryRouter>,
  )
}

describe('ForgotPasswordPage', () => {
  beforeEach(() => {
    getAuthCapabilitiesMock.mockReset()
    forgotPasswordMock.mockReset()

    getAuthCapabilitiesMock.mockResolvedValue({
      password: true,
      email_activation: false,
      email_code: false,
      sms_code: false,
      password_reset: true,
      admin_bootstrap_required: false,
      oauth_providers: [],
    })
    forgotPasswordMock.mockResolvedValue(undefined)

    vi.spyOn(message, 'success').mockImplementation(() => undefined as never)
    vi.spyOn(message, 'error').mockImplementation(() => undefined as never)
  })

  it('renders the reset request form when password reset is enabled', async () => {
    renderForgotPasswordPage()

    await waitFor(() => expect(getAuthCapabilitiesMock).toHaveBeenCalledTimes(1))

    expect(screen.getByRole('heading', { name: '忘记密码' })).toBeInTheDocument()
    expect(screen.getByPlaceholderText('邮箱地址')).toBeInTheDocument()
    expect(screen.getByRole('button', { name: '发送重置链接' })).toBeInTheDocument()
  })

  it('shows a warning state when password reset is disabled', async () => {
    getAuthCapabilitiesMock.mockResolvedValue({
      password: true,
      email_activation: false,
      email_code: false,
      sms_code: false,
      password_reset: false,
      admin_bootstrap_required: false,
      oauth_providers: [],
    })

    renderForgotPasswordPage()

    expect(await screen.findByText('密码重置未启用')).toBeInTheDocument()
    expect(screen.getByRole('button', { name: '返回登录' })).toBeInTheDocument()
  })

  it('falls back to the warning state when capability loading fails', async () => {
    getAuthCapabilitiesMock.mockRejectedValueOnce(new Error('capabilities failed'))

    renderForgotPasswordPage()

    expect(await screen.findByText('密码重置未启用')).toBeInTheDocument()
    expect(screen.queryByPlaceholderText('邮箱地址')).not.toBeInTheDocument()
  })

  it('submits the forgot-password request and shows the success summary', async () => {
    const user = userEvent.setup()
    renderForgotPasswordPage()

    await waitFor(() => expect(screen.getByPlaceholderText('邮箱地址')).toBeInTheDocument())

    await user.type(screen.getByPlaceholderText('邮箱地址'), 'user@example.com')
    await user.click(screen.getByRole('button', { name: '发送重置链接' }))

    await waitFor(() =>
      expect(forgotPasswordMock).toHaveBeenCalledWith({ email: 'user@example.com' }),
    )

    expect(await screen.findByText('邮件已发送')).toBeInTheDocument()
    expect(screen.getByText('user@example.com')).toBeInTheDocument()
    expect(message.success).toHaveBeenCalledWith('重置邮件已发送')
  })

  it('surfaces backend failures when sending the reset email', async () => {
    const user = userEvent.setup()
    forgotPasswordMock.mockRejectedValueOnce(new Error('send failed'))

    renderForgotPasswordPage()

    await waitFor(() => expect(screen.getByPlaceholderText('邮箱地址')).toBeInTheDocument())

    await user.type(screen.getByPlaceholderText('邮箱地址'), 'user@example.com')
    await user.click(screen.getByRole('button', { name: '发送重置链接' }))

    await waitFor(() => expect(message.error).toHaveBeenCalledWith('send failed'))
  })
})
