import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest'
import { render, screen } from '@testing-library/react'
import * as React from 'react'
import { SmartTooltip } from './smart-tooltip'

// Mock the useLayoutEffect hook for testing overflow scenarios
vi.mock('react', async () => {
  const actual = await vi.importActual('react')
  return {
    ...actual,
    useLayoutEffect: (callback: () => void) => {
      callback()
    },
  }
})

// Setup timers for useLayoutEffect timeout
beforeEach(() => {
  vi.useFakeTimers()
})

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

describe('SmartTooltip', () => {
  const defaultProps = {
    title: 'Test Label',
    children: ({ ref }: { ref: React.Ref<HTMLElement> }) => (
      <div ref={ref as React.Ref<HTMLDivElement>} data-testid='test-child'>
        Content
      </div>
    ),
  }

  test('renders correctly with child element', () => {
    render(<SmartTooltip {...defaultProps} />)
    const child = screen.getByTestId('test-child')
    expect(child).toBeTruthy()
    expect(child.textContent).toBe('Content')
  })

  test('passes ref to children function', () => {
    const childrenSpy = vi
      .fn()
      .mockReturnValue(<div data-testid='test-child'>Content</div>)
    render(<SmartTooltip title='Test Label'>{childrenSpy}</SmartTooltip>)

    expect(childrenSpy).toHaveBeenCalledTimes(1)
    // Just check that a ref was passed
    expect(childrenSpy).toHaveBeenCalled()
  })

  test('handles undefined title', () => {
    render(
      <SmartTooltip title={undefined}>
        {({ ref }) => (
          <div ref={ref as React.Ref<HTMLDivElement>} data-testid='test-child'>
            Content
          </div>
        )}
      </SmartTooltip>,
    )
    const child = screen.getByTestId('test-child')
    expect(child).toBeTruthy()
  })

  test('updates isOverflowing state when dependencies change', () => {
    const { rerender } = render(
      <SmartTooltip title='Test Label' dependencies={[1]}>
        {({ ref }) => (
          <div ref={ref as React.Ref<HTMLDivElement>} data-testid='test-child'>
            Content
          </div>
        )}
      </SmartTooltip>,
    )

    // Re-render with different dependencies to trigger useLayoutEffect
    rerender(
      <SmartTooltip title='Test Label' dependencies={[2]}>
        {({ ref }) => (
          <div ref={ref as React.Ref<HTMLDivElement>} data-testid='test-child'>
            Content
          </div>
        )}
      </SmartTooltip>,
    )

    expect(screen.getByTestId('test-child')).toBeTruthy()
  })

  test('tooltip behavior with overflowing content', () => {
    // Mock the ref.current properties to simulate overflow
    vi.spyOn(React, 'useRef').mockReturnValue({
      current: {
        scrollWidth: 100,
        clientWidth: 50,
        scrollHeight: 100,
        clientHeight: 50,
      },
    } as React.RefObject<HTMLDivElement>)

    render(<SmartTooltip {...defaultProps} />)

    // Force the timeout in useLayoutEffect to execute
    vi.runAllTimers()

    // Verify the tooltip has the correct title
    const tooltipWrapper = screen.getByTestId('test-child')
    expect(tooltipWrapper).toBeTruthy()
  })

  test('tooltip behavior with non-overflowing content', () => {
    // Mock the ref.current properties to simulate no overflow
    vi.spyOn(React, 'useRef').mockReturnValue({
      current: {
        scrollWidth: 50,
        clientWidth: 100,
        scrollHeight: 50,
        clientHeight: 100,
      },
    } as React.RefObject<HTMLDivElement>)

    render(<SmartTooltip {...defaultProps} />)

    // Force the timeout in useLayoutEffect to execute
    vi.runAllTimers()

    // Verify the tooltip doesn't have a title (since there's no overflow)
    const tooltipWrapper = screen.getByTestId('test-child')
    expect(tooltipWrapper).toBeTruthy()
    // Check that the title attribute is not set since content is not overflowing
    expect(tooltipWrapper?.getAttribute('title')).toBeFalsy()
  })

  test('accepts custom timeout parameter', () => {
    const customTimeout = 1000

    // Spy on setTimeout to verify it's called with the custom timeout
    const setTimeoutSpy = vi.spyOn(global, 'setTimeout')

    render(<SmartTooltip {...defaultProps} timeout={customTimeout} />)

    expect(setTimeoutSpy).toHaveBeenCalledWith(
      expect.any(Function),
      customTimeout,
    )

    setTimeoutSpy.mockRestore()
  })

  test('passes TooltipProps to the Tooltip component', () => {
    render(
      <SmartTooltip
        {...defaultProps}
        TooltipProps={{
          placement: 'bottom',
          arrow: true,
        }}
      />,
    )

    const tooltipWrapper = screen.getByTestId('test-child')
    expect(tooltipWrapper).toBeTruthy()
  })
})
