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 { Permission } from '@/types/permission'
import { PermissionFormModal } from './PermissionFormModal'

const createPermissionMock = vi.fn()
const updatePermissionMock = 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>

  const Select = Object.assign(({
    options = [],
    children,
  }: {
    options?: Array<{ value: number | string, label: ReactNode }>
    children?: ReactNode
  }) => (
    <select>
      {options.map((option) => (
        <option key={String(option.value)} value={String(option.value)}>
          {option.label}
        </option>
      ))}
      {children}
    </select>
  ), {
    Option: ({
      value,
      children,
    }: {
      value: string
      children?: ReactNode
    }) => <option value={value}>{children}</option>,
  })

  return {
    Modal: ({
      open,
      title,
      onOk,
      onCancel,
      children,
    }: {
      open: boolean
      title: ReactNode
      onOk?: () => void
      onCancel?: () => void
      children?: ReactNode
    }) => (
      open ? (
        <div data-testid="permission-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: ({
      placeholder,
      disabled,
    }: {
      placeholder?: string
      disabled?: boolean
    }) => <input placeholder={placeholder} disabled={disabled} />,
    InputNumber: () => <input type="number" />,
    Select,
    message: {
      success: (content: string) => messageSuccessMock(content),
      error: (content: string) => messageErrorMock(content),
    },
  }
})

vi.mock('@/services/permissions', () => ({
  createPermission: (payload: unknown) => createPermissionMock(payload),
  updatePermission: (id: number, payload: unknown) => updatePermissionMock(id, payload),
}))

function buildPermission(
  id: number,
  name: string,
  code: string,
  parentId: number | null = null,
  children?: Permission[],
): Permission {
  return {
    id,
    name,
    code,
    type: 'menu',
    parent_id: parentId,
    path: `/admin/${code}`,
    icon: `${code}-icon`,
    sort: id,
    status: 1,
    children,
  }
}

describe('PermissionFormModal', () => {
  beforeEach(() => {
    createPermissionMock.mockReset()
    updatePermissionMock.mockReset()
    messageSuccessMock.mockReset()
    messageErrorMock.mockReset()
    formApi.validateFields.mockReset()
    formApi.setFieldsValue.mockReset()
    formApi.resetFields.mockReset()
  })

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

  it('creates permissions, applies parent defaults, and resets on cancel', async () => {
    const user = userEvent.setup()
    const parentPermission = buildPermission(1, 'Dashboard', 'dashboard')
    const permissions = [
      parentPermission,
      buildPermission(2, 'Users', 'user:manage', 1),
    ]
    const onSuccess = vi.fn()
    const onClose = vi.fn()

    formApi.validateFields.mockResolvedValue({
      name: 'Audit API',
      code: 'audit:read',
      type: 'api',
      parent_id: 1,
      path: '/admin/audit',
      icon: 'AuditOutlined',
      sort: undefined,
    })
    createPermissionMock.mockResolvedValue(undefined)

    render(
      <PermissionFormModal
        open
        permission={null}
        parentPermission={parentPermission}
        permissions={permissions}
        onSuccess={onSuccess}
        onClose={onClose}
      />,
    )

    expect(formApi.resetFields).toHaveBeenCalled()
    expect(formApi.setFieldsValue).toHaveBeenCalledWith({ parent_id: 1 })
    expect(screen.getByText('创建子权限 - Dashboard')).toBeInTheDocument()
    expect(screen.getByText('Dashboard')).toBeInTheDocument()
    expect(screen.getByText('Users')).toBeInTheDocument()

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

    await waitFor(() => expect(createPermissionMock).toHaveBeenCalledWith({
      name: 'Audit API',
      code: 'audit:read',
      type: 'api',
      parent_id: 1,
      path: '/admin/audit',
      icon: 'AuditOutlined',
      sort: 0,
    }))
    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, excludes self from parent options, and updates editable fields only', async () => {
    const user = userEvent.setup()
    const permission = buildPermission(2, 'Users', 'user:manage', 1)
    const permissions = [
      buildPermission(1, 'Dashboard', 'dashboard', null, [permission]),
      buildPermission(3, 'Reports', 'reports:view'),
    ]

    formApi.validateFields.mockResolvedValue({
      name: 'Users Updated',
      code: 'ignored-code',
      type: 'menu',
      parent_id: 3,
      path: '/admin/users',
      icon: 'UserOutlined',
      sort: 20,
    })
    updatePermissionMock.mockResolvedValue(undefined)

    render(
      <PermissionFormModal
        open
        permission={permission}
        parentPermission={null}
        permissions={permissions}
        onSuccess={vi.fn()}
        onClose={vi.fn()}
      />,
    )

    expect(formApi.setFieldsValue).toHaveBeenCalledWith({
      name: 'Users',
      code: 'user:manage',
      type: 'menu',
      parent_id: 1,
      path: '/admin/user:manage',
      icon: 'user:manage-icon',
      sort: 2,
    })
    expect(screen.getByText('编辑权限')).toBeInTheDocument()
    expect(screen.getByText('Dashboard')).toBeInTheDocument()
    expect(screen.getByText('Reports')).toBeInTheDocument()
    expect(screen.queryByText('Users')).not.toBeInTheDocument()

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

    await waitFor(() => expect(updatePermissionMock).toHaveBeenCalledWith(2, {
      name: 'Users Updated',
      path: '/admin/users',
      icon: 'UserOutlined',
      sort: 20,
    }))
    expect(messageSuccessMock).toHaveBeenCalledWith('权限已更新')
  })

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

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

    const { rerender } = render(
      <PermissionFormModal
        open
        permission={null}
        parentPermission={null}
        permissions={[]}
        onSuccess={vi.fn()}
        onClose={vi.fn()}
      />,
    )

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

    await waitFor(() => expect(createPermissionMock).not.toHaveBeenCalled())
    expect(updatePermissionMock).not.toHaveBeenCalled()
    expect(messageErrorMock).not.toHaveBeenCalled()

    formApi.validateFields.mockResolvedValueOnce({
      name: 'Broken Permission',
      code: 'broken:permission',
      type: 'api',
      parent_id: null,
      path: '',
      icon: '',
      sort: 0,
    })
    createPermissionMock.mockRejectedValueOnce(new Error('save failed'))

    rerender(
      <PermissionFormModal
        open
        permission={null}
        parentPermission={null}
        permissions={[]}
        onSuccess={vi.fn()}
        onClose={vi.fn()}
      />,
    )

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

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