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/role'
import { RoleFormModal } from './RoleFormModal'

const createRoleMock = vi.fn()
const updateRoleMock = 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 {
    Modal: ({
      open,
      title,
      onOk,
      onCancel,
      children,
    }: {
      open: boolean
      title: ReactNode
      onOk?: () => void
      onCancel?: () => void
      children?: ReactNode
    }) => (
      open ? (
        <div data-testid="role-form-modal-root">
          <h1>{title}</h1>
          {children}
          <button type="button" onClick={onOk}>modal ok</button>
          <button type="button" onClick={onCancel}>modal cancel</button>
        </div>
      ) : null
    ),
    Form: Object.assign(Form, {
      useForm: () => [formApi],
      Item: ({ children }: { children?: ReactNode }) => <div>{children}</div>,
    }),
    Input: Object.assign(({
      placeholder,
      disabled,
    }: {
      placeholder?: string
      disabled?: boolean
    }) => <input placeholder={placeholder} disabled={disabled} />, {
      TextArea: ({ placeholder }: { placeholder?: string }) => <textarea placeholder={placeholder} />,
    }),
    Switch: () => <button type="button">switch</button>,
    message: {
      success: (content: string) => messageSuccessMock(content),
      error: (content: string) => messageErrorMock(content),
    },
  }
})

vi.mock('@/services/roles', () => ({
  createRole: (payload: unknown) => createRoleMock(payload),
  updateRole: (id: number, payload: unknown) => updateRoleMock(id, payload),
}))

function buildRole(): Role {
  return {
    id: 7,
    name: 'Editor',
    code: 'editor',
    description: 'editor role',
    is_system: false,
    is_default: true,
    status: 1,
  }
}

describe('RoleFormModal', () => {
  beforeEach(() => {
    createRoleMock.mockReset()
    updateRoleMock.mockReset()
    messageSuccessMock.mockReset()
    messageErrorMock.mockReset()
    formApi.validateFields.mockReset()
    formApi.setFieldsValue.mockReset()
    formApi.resetFields.mockReset()
  })

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

  it('creates a role in create mode and resets/close on cancel', async () => {
    const user = userEvent.setup()
    const onSuccess = vi.fn()
    const onClose = vi.fn()

    formApi.validateFields.mockResolvedValue({
      name: 'Auditor',
      code: 'auditor',
      description: 'auditor role',
      is_default: false,
    })
    createRoleMock.mockResolvedValue(undefined)

    render(
      <RoleFormModal
        open
        role={null}
        onSuccess={onSuccess}
        onClose={onClose}
      />,
    )

    expect(formApi.resetFields).toHaveBeenCalled()
    expect(screen.getByText('创建角色')).toBeInTheDocument()

    await user.click(screen.getByRole('button', { name: 'modal ok' }))

    await waitFor(() => expect(createRoleMock).toHaveBeenCalledWith({
      name: 'Auditor',
      code: 'auditor',
      description: 'auditor role',
      is_default: false,
    }))
    expect(messageSuccessMock).toHaveBeenCalledWith('角色已创建')
    expect(onSuccess).toHaveBeenCalledTimes(1)

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

  it('prefills edit mode and updates the role without code changes', async () => {
    const user = userEvent.setup()
    const role = buildRole()
    const onSuccess = vi.fn()

    formApi.validateFields.mockResolvedValue({
      name: 'Editor Updated',
      code: 'ignored-code',
      description: 'updated description',
      is_default: false,
    })
    updateRoleMock.mockResolvedValue(undefined)

    render(
      <RoleFormModal
        open
        role={role}
        onSuccess={onSuccess}
        onClose={vi.fn()}
      />,
    )

    expect(formApi.setFieldsValue).toHaveBeenCalledWith({
      name: 'Editor',
      code: 'editor',
      description: 'editor role',
      is_default: true,
    })
    expect(screen.getByText('编辑角色')).toBeInTheDocument()

    await user.click(screen.getByRole('button', { name: 'modal ok' }))

    await waitFor(() => expect(updateRoleMock).toHaveBeenCalledWith(7, {
      name: 'Editor Updated',
      description: 'updated description',
      is_default: false,
    }))
    expect(messageSuccessMock).toHaveBeenCalledWith('角色已更新')
    expect(onSuccess).toHaveBeenCalledTimes(1)
  })

  it('swallows form validation errors and surfaces service failures', async () => {
    const user = userEvent.setup()

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

    const { rerender } = render(
      <RoleFormModal
        open
        role={null}
        onSuccess={vi.fn()}
        onClose={vi.fn()}
      />,
    )

    await user.click(screen.getByRole('button', { name: 'modal ok' }))

    await waitFor(() => expect(createRoleMock).not.toHaveBeenCalled())
    expect(updateRoleMock).not.toHaveBeenCalled()
    expect(messageErrorMock).not.toHaveBeenCalled()

    formApi.validateFields.mockResolvedValueOnce({
      name: 'Broken Role',
      code: 'broken_role',
      description: '',
      is_default: false,
    })
    createRoleMock.mockRejectedValueOnce(new Error('save failed'))

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

    await user.click(screen.getByRole('button', { name: 'modal ok' }))

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