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

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

const getAuthCapabilitiesMock = vi.fn<() => Promise<AuthCapabilities>>()
const activateEmailMock = vi.fn<(token: string) => Promise<unknown>>()
const resendActivationEmailMock = vi.fn<(payload: { email: string }) => Promise<{ message: string }>>()

vi.mock('@/services/auth', () => ({
  getAuthCapabilities: () => getAuthCapabilitiesMock(),
  activateEmail: (token: string) => activateEmailMock(token),
  resendActivationEmail: (payload: { email: string }) => resendActivationEmailMock(payload),
}))

function renderActivateAccountPage(initialEntry: string) {
  return render(
    <MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={[initialEntry]}>
      <Routes>
        <Route path="/activate-account" element={<ActivateAccountPage />} />
      </Routes>
    </MemoryRouter>,
  )
}

describe('ActivateAccountPage', () => {
  beforeEach(() => {
    getAuthCapabilitiesMock.mockReset()
    activateEmailMock.mockReset()
    resendActivationEmailMock.mockReset()

    getAuthCapabilitiesMock.mockResolvedValue({
      password: true,
      email_activation: true,
      email_code: false,
      sms_code: false,
      password_reset: false,
      admin_bootstrap_required: false,
      oauth_providers: [],
    })
    activateEmailMock.mockResolvedValue({ message: 'account activated successfully' })
    resendActivationEmailMock.mockResolvedValue({
      message: 'if the email exists, an activation email will be sent shortly',
    })
  })

  it('activates the account when a token is present in the URL', async () => {
    renderActivateAccountPage('/activate-account?token=token-123')

    await waitFor(() => expect(activateEmailMock).toHaveBeenCalledWith('token-123'))

    expect(await screen.findByText('邮箱验证成功')).toBeInTheDocument()
    expect(screen.getByRole('button', { name: '立即登录' })).toBeInTheDocument()
  })

  it('shows the resend form when activation fails', async () => {
    activateEmailMock.mockRejectedValueOnce(new Error('activation token expired or missing'))

    renderActivateAccountPage('/activate-account?token=expired-token&email=user@example.com')

    await waitFor(() => expect(activateEmailMock).toHaveBeenCalledWith('expired-token'))

    expect(await screen.findByText('激活链接不可用')).toBeInTheDocument()
    expect(screen.getByPlaceholderText('邮箱地址')).toHaveValue('user@example.com')
  })

  it('resends the activation email from the public activation page', async () => {
    const user = userEvent.setup()
    renderActivateAccountPage('/activate-account?email=user@example.com')

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

    await user.clear(screen.getByPlaceholderText('邮箱地址'))
    await user.type(screen.getByPlaceholderText('邮箱地址'), 'user@example.com')
    await user.click(screen.getByRole('button', { name: '重新发送激活邮件' }))

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

    expect(await screen.findByText(/请检查收件箱和垃圾邮件目录/)).toBeInTheDocument()
    expect(screen.getByText('user@example.com')).toBeInTheDocument()
  })

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

    renderActivateAccountPage('/activate-account')

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

    expect(await screen.findByText('邮箱激活未启用')).toBeInTheDocument()
    expect(screen.queryByRole('button', { name: '重新发送激活邮件' })).not.toBeInTheDocument()
  })
})
