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

import { PageEmpty, PageError, PageLoading } from './PageState'

vi.mock('antd', () => ({
  Button: ({
    children,
    onClick,
    icon,
    htmlType,
    ...props
  }: {
    children?: ReactNode
    onClick?: () => void
    icon?: ReactNode
    htmlType?: 'button' | 'submit' | 'reset'
    [key: string]: unknown
  }) => {
    void icon

    return (
      <button type={htmlType ?? 'button'} onClick={onClick} {...props}>
        {children}
      </button>
    )
  },
  Empty: ({
    description,
    children,
  }: {
    description?: ReactNode
    children?: ReactNode
  }) => (
    <div data-testid="empty">
      <div data-testid="empty-description">{description}</div>
      {children}
    </div>
  ),
  Result: ({
    status,
    title,
    subTitle,
    extra,
  }: {
    status?: string
    title?: ReactNode
    subTitle?: ReactNode
    extra?: ReactNode | ReactNode[]
  }) => (
    <div data-testid="result" data-status={status}>
      <div>{title}</div>
      <div>{subTitle}</div>
      <div>{extra}</div>
    </div>
  ),
  Spin: ({
    size,
    tip,
    children,
  }: {
    size?: string
    tip?: ReactNode
    children?: ReactNode
  }) => (
    <div data-testid="spin" data-size={size}>
      <span>{tip}</span>
      {children}
    </div>
  ),
}))

vi.mock('@ant-design/icons', () => ({
  PlusOutlined: () => <span>plus-icon</span>,
  ReloadOutlined: () => <span>reload-icon</span>,
}))

describe('PageState', () => {
  it('renders PageLoading with both default and custom tips', () => {
    render(
      <>
        <PageLoading />
        <PageLoading tip="loading-dashboard" />
      </>,
    )

    expect(screen.getAllByTestId('spin')).toHaveLength(2)
    expect(screen.getByText('loading-dashboard')).toBeInTheDocument()
  })

  it('renders PageEmpty without an action when the action handler is incomplete', () => {
    render(<PageEmpty description="no data" actionText="create now" />)

    expect(screen.getByTestId('empty-description')).toHaveTextContent('no data')
    expect(screen.queryByRole('button')).not.toBeInTheDocument()
  })

  it('renders PageEmpty action button and invokes the handler when clicked', async () => {
    const user = userEvent.setup()
    const onAction = vi.fn()

    render(
      <PageEmpty
        description="empty table"
        actionText="add first item"
        onAction={onAction}
        actionProps={{ 'data-action': 'create' }}
      />,
    )

    const button = screen.getByRole('button', { name: 'add first item' })

    expect(button).toHaveAttribute('data-action', 'create')

    await user.click(button)

    expect(onAction).toHaveBeenCalledTimes(1)
  })

  it('renders PageError defaults without a retry button when onRetry is absent', () => {
    render(<PageError />)

    expect(screen.getByTestId('result')).toHaveAttribute('data-status', 'error')
    expect(screen.queryByRole('button')).not.toBeInTheDocument()
  })

  it('renders PageError retry and extra actions when provided', async () => {
    const user = userEvent.setup()
    const onRetry = vi.fn()

    render(
      <PageError
        title="load failed"
        description="service unavailable"
        retryText="retry now"
        onRetry={onRetry}
        extra={<span>contact support</span>}
      />,
    )

    expect(screen.getByText('load failed')).toBeInTheDocument()
    expect(screen.getByText('service unavailable')).toBeInTheDocument()
    expect(screen.getByText('contact support')).toBeInTheDocument()

    await user.click(screen.getByRole('button', { name: 'retry now' }))

    expect(onRetry).toHaveBeenCalledTimes(1)
  })
})
