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

import type { User } from '@/types/user'
import { UserEditDrawer } from './UserEditDrawer'

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

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

  return {
    Button: ({
      children,
      onClick,
    }: {
      children?: ReactNode
      onClick?: () => void
    }) => (
      <button type="button" onClick={onClick}>
        {children}
      </button>
    ),
    DatePicker: ({
      placeholder,
      disabledDate,
    }: {
      placeholder?: string
      disabledDate?: (value: ReturnType<typeof dayjs> | null) => boolean
    }) => (
      <div>
        <input placeholder={placeholder} readOnly />
        <span>{`disabled-future:${String(disabledDate?.(dayjs('2999-01-01')))}`}</span>
        <span>{`disabled-empty:${String(Boolean(disabledDate?.(null)))}`}</span>
      </div>
    ),
    Drawer: ({
      open,
      title,
      onClose,
      footer,
      children,
    }: {
      open: boolean
      title?: ReactNode
      onClose?: () => void
      footer?: ReactNode
      children?: ReactNode
    }) => (
      open ? (
        <div data-testid="user-edit-drawer-root">
          <h1>{title}</h1>
          {children}
          {footer}
          <button type="button" onClick={onClose}>drawer close</button>
        </div>
      ) : null
    ),
    Form: Object.assign(Form, {
      useForm: () => [formApi],
      Item: ({
        label,
        children,
      }: {
        label?: ReactNode
        children?: ReactNode
      }) => (
        <div>
          {label ? <span>{label}</span> : null}
          {children}
        </div>
      ),
    }),
    Input: Object.assign(({
      placeholder,
      value,
      disabled,
    }: {
      placeholder?: string
      value?: string
      disabled?: boolean
    }) => (
      <input
        placeholder={placeholder}
        value={value ?? ''}
        disabled={disabled}
        readOnly
      />
    ), {
      TextArea: ({ placeholder }: { placeholder?: string }) => (
        <textarea placeholder={placeholder} readOnly />
      ),
    }),
    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', () => ({
  updateUser: (userId: number, payload: unknown) => updateUserMock(userId, payload),
}))

function buildUser(): User {
  return {
    id: 7,
    username: 'alice',
    email: 'alice@example.com',
    phone: '13800000007',
    nickname: 'Alice',
    avatar: '',
    gender: 2,
    birthday: '2026-03-12',
    region: 'Shanghai',
    bio: 'hello world',
    status: 1,
    last_login_at: '2026-03-27 10:00:00',
    last_login_ip: '127.0.0.1',
    created_at: '2026-03-01 10:00:00',
    updated_at: '2026-03-20 10:00:00',
  }
}

describe('UserEditDrawer', () => {
  beforeEach(() => {
    updateUserMock.mockReset()
    messageSuccessMock.mockReset()
    messageErrorMock.mockReset()
    formApi.validateFields.mockReset()
    formApi.setFieldsValue.mockReset()
    formApi.resetFields.mockReset()
  })

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

  it('prefills the form and submits normalized values', async () => {
    const user = userEvent.setup()
    const record = buildUser()
    const onSuccess = vi.fn()

    formApi.validateFields.mockResolvedValue({
      email: 'updated@example.com',
      phone: '13800000008',
      nickname: 'Alice Updated',
      gender: 1,
      birthday: dayjs('2026-03-15'),
      region: 'Beijing',
      bio: 'updated bio',
    })
    updateUserMock.mockResolvedValue(undefined)

    render(
      <UserEditDrawer
        open
        user={record}
        onSuccess={onSuccess}
        onClose={vi.fn()}
      />,
    )

    expect(formApi.setFieldsValue).toHaveBeenCalledWith({
      email: 'alice@example.com',
      phone: '13800000007',
      nickname: 'Alice',
      gender: 2,
      birthday: expect.any(Object),
      region: 'Shanghai',
      bio: 'hello world',
    })
    const hydratedBirthday = formApi.setFieldsValue.mock.calls[0][0].birthday
    expect(dayjs.isDayjs(hydratedBirthday)).toBe(true)
    expect(hydratedBirthday.format('YYYY-MM-DD')).toBe('2026-03-12')
    expect(screen.getByText('disabled-future:true')).toBeInTheDocument()
    expect(screen.getByText('disabled-empty:false')).toBeInTheDocument()

    await user.click(screen.getByRole('button', { name: '保存' }))

    await waitFor(() => expect(updateUserMock).toHaveBeenCalledWith(7, {
      email: 'updated@example.com',
      phone: '13800000008',
      nickname: 'Alice Updated',
      gender: 1,
      birthday: '2026-03-15',
      region: 'Beijing',
      bio: 'updated bio',
    }))
    expect(messageSuccessMock).toHaveBeenCalledTimes(1)
    expect(onSuccess).toHaveBeenCalledTimes(1)
  })

  it('swallows validation errors and returns early when user data is missing', async () => {
    const user = userEvent.setup()

    formApi.validateFields.mockRejectedValueOnce({ errorFields: [{ name: ['email'] }] })

    const { rerender } = render(
      <UserEditDrawer
        open
        user={buildUser()}
        onSuccess={vi.fn()}
        onClose={vi.fn()}
      />,
    )

    await user.click(screen.getByRole('button', { name: '保存' }))

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

    rerender(
      <UserEditDrawer
        open
        user={null}
        onSuccess={vi.fn()}
        onClose={vi.fn()}
      />,
    )

    await user.click(screen.getByRole('button', { name: '保存' }))
    expect(formApi.validateFields).toHaveBeenCalledTimes(1)
  })

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

    formApi.validateFields.mockResolvedValue({
      email: 'broken@example.com',
      phone: '13800000009',
      nickname: 'Broken',
      gender: 0,
      birthday: undefined,
      region: 'Hangzhou',
      bio: 'broken bio',
    })
    updateUserMock.mockRejectedValueOnce(new Error('update failed'))

    render(
      <UserEditDrawer
        open
        user={buildUser()}
        onSuccess={vi.fn()}
        onClose={onClose}
      />,
    )

    await user.click(screen.getByRole('button', { name: '保存' }))

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

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