import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { ConfirmDialog } from '../ConfirmDialog'

// Mock Radix portal to render inline
jest.mock('@radix-ui/react-dialog', () => {
  const actual = jest.requireActual('@radix-ui/react-dialog')
  return {
    ...actual,
    Portal: ({ children }: { children: React.ReactNode }) => <>{children}</>,
  }
})

describe('ConfirmDialog', () => {
  const defaultProps = {
    open: true,
    onClose: jest.fn(),
    onConfirm: jest.fn(),
    title: 'Delete item?',
  }

  beforeEach(() => {
    jest.clearAllMocks()
  })

  it('renders title', () => {
    render(<ConfirmDialog {...defaultProps} />)
    expect(screen.getByText('Delete item?')).toBeInTheDocument()
  })

  it('renders string description', () => {
    render(
      <ConfirmDialog {...defaultProps} description="This cannot be undone." />
    )
    expect(screen.getByText('This cannot be undone.')).toBeInTheDocument()
  })

  it('renders ReactNode description', () => {
    render(
      <ConfirmDialog
        {...defaultProps}
        description={<span data-testid="custom">Custom content</span>}
      />
    )
    expect(screen.getByTestId('custom')).toBeInTheDocument()
  })

  it('renders default button labels', () => {
    render(<ConfirmDialog {...defaultProps} />)
    expect(screen.getByText('Confirm')).toBeInTheDocument()
    expect(screen.getByText('Cancel')).toBeInTheDocument()
  })

  it('renders custom button labels', () => {
    render(
      <ConfirmDialog
        {...defaultProps}
        confirmLabel="Delete"
        cancelLabel="Keep"
      />
    )
    expect(screen.getByText('Delete')).toBeInTheDocument()
    expect(screen.getByText('Keep')).toBeInTheDocument()
  })

  it('calls onConfirm when confirm button is clicked', () => {
    render(<ConfirmDialog {...defaultProps} />)
    fireEvent.click(screen.getByText('Confirm'))
    expect(defaultProps.onConfirm).toHaveBeenCalledTimes(1)
  })

  it('calls onClose when cancel button is clicked', () => {
    render(<ConfirmDialog {...defaultProps} />)
    fireEvent.click(screen.getByText('Cancel'))
    expect(defaultProps.onClose).toHaveBeenCalledTimes(1)
  })

  it('disables buttons when loading', () => {
    render(<ConfirmDialog {...defaultProps} loading />)
    const confirmBtn = screen.getByText('Confirm').closest('button')
    const cancelBtn = screen.getByText('Cancel').closest('button')
    expect(confirmBtn).toBeDisabled()
    expect(cancelBtn).toBeDisabled()
  })

  it('shows spinner when loading', () => {
    const { container } = render(<ConfirmDialog {...defaultProps} loading />)
    const spinner = container.querySelector('.animate-spin')
    expect(spinner).toBeInTheDocument()
  })

  it('does not call onClose when cancel clicked during loading', () => {
    render(<ConfirmDialog {...defaultProps} loading />)
    // The button is disabled so the click handler won't fire,
    // but also handleClose guards against it
    const cancelBtn = screen.getByText('Cancel').closest('button')!
    fireEvent.click(cancelBtn)
    expect(defaultProps.onClose).not.toHaveBeenCalled()
  })

  it('applies destructive variant to confirm button', () => {
    render(<ConfirmDialog {...defaultProps} destructive />)
    const confirmBtn = screen.getByText('Confirm').closest('button')
    expect(confirmBtn?.className).toMatch(/destructive/)
  })

  it('applies default variant when not destructive', () => {
    render(<ConfirmDialog {...defaultProps} />)
    const confirmBtn = screen.getByText('Confirm').closest('button')
    expect(confirmBtn?.className).not.toMatch(/destructive/)
  })

  it('does not render body when no description provided', () => {
    const { container } = render(<ConfirmDialog {...defaultProps} />)
    // Body has class py-4, should not exist
    expect(container.querySelector('.py-4')).not.toBeInTheDocument()
  })

  it('handles async onConfirm', async () => {
    const asyncConfirm = jest.fn().mockResolvedValue(undefined)
    render(<ConfirmDialog {...defaultProps} onConfirm={asyncConfirm} />)
    fireEvent.click(screen.getByText('Confirm'))
    await waitFor(() => {
      expect(asyncConfirm).toHaveBeenCalledTimes(1)
    })
  })
})
