import type { ReactNode } from 'react'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

import type { Role } from '@/types'
import { CreateUserModal } from './CreateUserModal'

const createUserMock = vi.fn()
const messageSuccessMock = vi.fn()
const messageErrorMock = vi.fn()
const formApi = {
  validateFields: vi.fn(),
  setFieldsValue: vi.fn(),
  resetFields: vi.fn(),
  setFieldValue: vi.fn(),
}

const watchValues = {
  email: undefined as string | undefined,
  send_activation_email: false,
}

vi.mock('antd', () => {
  const Form = ({
    children,
  }: {
    children?: ReactNode
  }) => <form>{children}</form>

  return {
    Alert: ({
      message,
    }: {
      message?: ReactNode
    }) => <div>{message}</div>,
    Button: ({
      children,
      onClick,
    }: {
      children?: ReactNode
      onClick?: () => void
    }) => (
      <button type="button" onClick={onClick}>
        {children}
      </button>
    ),
    Checkbox: ({
      children,
      disabled,
    }: {
      children?: ReactNode
      disabled?: boolean
    }) => <label>{`${String(disabled ?? false)}:${children ?? ''}`}</label>,
    Form: Object.assign(Form, {
      useForm: () => [formApi],
      useWatch: (name: string) => watchValues[name as keyof typeof watchValues],
      Item: ({
        label,
        children,
      }: {
        label?: ReactNode
        children?: ReactNode
      }) => (
        <div>
          {label ? <span>{label}</span> : null}
          {children}
        </div>
      ),
    }),
    Input: Object.assign(({
      placeholder,
    }: {
      placeholder?: string
    }) => <input placeholder={placeholder} readOnly />, {
      Password: ({ placeholder }: { placeholder?: string }) => (
        <input placeholder={placeholder} readOnly type="password" />
      ),
    }),
    Modal: ({
      open,
      title,
      onCancel,
      footer,
      children,
    }: {
      open: boolean
      title?: ReactNode
      onCancel?: () => void
      footer?: ReactNode
      children?: ReactNode
    }) => (
      open ? (
        <div data-testid="create-user-modal-root">
          <h1>{title}</h1>
          {children}
          {footer}
          <button type="button" onClick={onCancel}>modal close</button>
        </div>
      ) : null
    ),
    Select: ({
      placeholder,
      options = [],
    }: {
      placeholder?: string
      options?: Array<{ value: number | string, label: ReactNode }>
    }) => (
      <select aria-label={placeholder ?? 'select'}>
        {options.map((option) => (
          <option key={String(option.value)} value={String(option.value)}>
            {option.label}
          </option>
        ))}
      </select>
    ),
    Space: ({ children }: { children?: ReactNode }) => <div>{children}</div>,
    message: {
      success: (content: string) => messageSuccessMock(content),
      error: (content: string) => messageErrorMock(content),
    },
  }
})

vi.mock('@/services/users', () => ({
  createUser: (payload: unknown) => createUserMock(payload),
}))

function buildRoles(): Role[] {
  return [
    {
      id: 1,
      name: 'Admin',
      code: 'admin',
      description: 'system admin',
      is_system: true,
      is_default: false,
      status: 1,
    },
  ]
}

describe('CreateUserModal behavior', () => {
  beforeEach(() => {
    createUserMock.mockReset()
    messageSuccessMock.mockReset()
    messageErrorMock.mockReset()
    formApi.validateFields.mockReset()
    formApi.setFieldsValue.mockReset()
    formApi.resetFields.mockReset()
    formApi.setFieldValue.mockReset()
    watchValues.email = undefined
    watchValues.send_activation_email = false
  })

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

  it('hydrates defaults on open, clears activation email without an email, and resets when closed', () => {
    watchValues.send_activation_email = true

    const { rerender } = render(
      <CreateUserModal
        open
        roles={buildRoles()}
        onSuccess={vi.fn()}
        onClose={vi.fn()}
      />,
    )

    expect(formApi.setFieldsValue).toHaveBeenCalledWith({
      status: 1,
      role_ids: [],
      send_activation_email: false,
    })
    expect(formApi.setFieldValue).toHaveBeenCalledWith('send_activation_email', false)
    expect(screen.getByText('true:创建后发送激活邮件')).toBeInTheDocument()

    rerender(
      <CreateUserModal
        open={false}
        roles={buildRoles()}
        onSuccess={vi.fn()}
        onClose={vi.fn()}
      />,
    )

    expect(formApi.resetFields).toHaveBeenCalledTimes(1)
  })

  it('swallows validation errors, surfaces service failures, and resets on close', async () => {
    const user = userEvent.setup()
    const onClose = vi.fn()

    watchValues.email = 'user@example.com'
    watchValues.send_activation_email = false
    formApi.validateFields.mockRejectedValueOnce({ errorFields: [{ name: ['username'] }] })

    const { rerender } = render(
      <CreateUserModal
        open
        roles={buildRoles()}
        onSuccess={vi.fn()}
        onClose={onClose}
      />,
    )

    await user.click(screen.getByRole('button', { name: '创建用户' }))

    await waitFor(() => expect(createUserMock).not.toHaveBeenCalled())
    expect(messageErrorMock).not.toHaveBeenCalled()

    formApi.validateFields.mockResolvedValueOnce({
      username: 'broken-user',
      password: 'Pass123!@#',
      email: 'broken@example.com',
      phone: '13800000011',
      nickname: 'Broken',
      role_ids: [],
      status: 1,
      send_activation_email: false,
    })
    createUserMock.mockRejectedValueOnce(new Error('create failed'))

    rerender(
      <CreateUserModal
        open
        roles={buildRoles()}
        onSuccess={vi.fn()}
        onClose={onClose}
      />,
    )

    await user.click(screen.getByRole('button', { name: '创建用户' }))

    await waitFor(() => expect(messageErrorMock).toHaveBeenCalledWith('create failed'))

    await user.click(screen.getByRole('button', { name: '取消' }))
    expect(formApi.resetFields).toHaveBeenCalled()
    expect(onClose).toHaveBeenCalledTimes(1)
  })
})
