import { fireEvent, render, screen } from '@testing-library/react'
import { act } from 'react'
import { afterEach, beforeEach, describe, expect, it, type Mock, vi } from 'vitest'

import Scrollbar from '../Scrollbar'

// Mock lodash throttle
vi.mock('lodash', async () => {
  const actual = await import('lodash')
  return {
    ...actual,
    throttle: vi.fn((fn) => {
      // 简单地直接返回函数，不实际执行节流
      const throttled = (...args: any[]) => fn(...args)
      throttled.cancel = vi.fn()
      return throttled
    })
  }
})

describe('Scrollbar', () => {
  beforeEach(() => {
    // 使用 fake timers
    vi.useFakeTimers()
  })

  afterEach(() => {
    // 恢复真实的 timers
    vi.restoreAllMocks()
    vi.useRealTimers()
  })

  describe('rendering', () => {
    it('should render children correctly', () => {
      render(
        <Scrollbar data-testid="scrollbar">
          <div data-testid="child">测试内容</div>
        </Scrollbar>
      )

      const child = screen.getByTestId('child')
      expect(child).toBeDefined()
      expect(child.textContent).toBe('测试内容')
    })

    it('should pass custom props to container', () => {
      render(
        <Scrollbar data-testid="scrollbar" className="custom-class">
          内容
        </Scrollbar>
      )

      const scrollbar = screen.getByTestId('scrollbar')
      expect(scrollbar.className).toContain('custom-class')
    })

    it('should match default styled snapshot', () => {
      const { container } = render(<Scrollbar data-testid="scrollbar">内容</Scrollbar>)
      expect(container.firstChild).toMatchSnapshot()
    })
  })

  describe('scrolling behavior', () => {
    it('should update isScrolling state when scrolled', () => {
      render(<Scrollbar data-testid="scrollbar">内容</Scrollbar>)

      const scrollbar = screen.getByTestId('scrollbar')

      // 初始状态下应该不是滚动状态
      expect(scrollbar.getAttribute('isScrolling')).toBeFalsy()

      // 触发滚动
      fireEvent.scroll(scrollbar)

      // 由于 isScrolling 是组件内部状态，不直接反映在 DOM 属性上
      // 但可以检查模拟的事件处理是否被调用
      expect(scrollbar).toBeDefined()
    })

    it('should reset isScrolling after timeout', () => {
      render(<Scrollbar data-testid="scrollbar">内容</Scrollbar>)

      const scrollbar = screen.getByTestId('scrollbar')

      // 触发滚动
      fireEvent.scroll(scrollbar)

      // 前进时间但不超过timeout
      act(() => {
        vi.advanceTimersByTime(1000)
      })

      // 前进超过timeout
      act(() => {
        vi.advanceTimersByTime(600)
      })

      // 不测试样式，这里只检查组件是否存在
      expect(scrollbar).toBeDefined()
    })

    it('should reset timeout on continuous scrolling', () => {
      const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout')

      render(<Scrollbar data-testid="scrollbar">内容</Scrollbar>)

      const scrollbar = screen.getByTestId('scrollbar')

      // 第一次滚动
      fireEvent.scroll(scrollbar)

      // 前进一部分时间
      act(() => {
        vi.advanceTimersByTime(800)
      })

      // 再次滚动
      fireEvent.scroll(scrollbar)

      // clearTimeout 应该被调用，因为在第二次滚动时会清除之前的定时器
      expect(clearTimeoutSpy).toHaveBeenCalled()
    })
  })

  describe('throttling', () => {
    it('should use throttled scroll handler', async () => {
      const { throttle } = await import('lodash')

      render(<Scrollbar data-testid="scrollbar">内容</Scrollbar>)

      // 验证 throttle 被调用
      expect(throttle).toHaveBeenCalled()
      // 验证 throttle 调用时使用了 100ms 延迟和正确的选项
      expect(throttle).toHaveBeenCalledWith(expect.any(Function), 100, { leading: true, trailing: true })
    })
  })

  describe('cleanup', () => {
    it('should clear timeout and cancel throttle on unmount', async () => {
      const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout')

      const { unmount } = render(<Scrollbar data-testid="scrollbar">内容</Scrollbar>)

      const scrollbar = screen.getByTestId('scrollbar')

      // 触发滚动设置定时器
      fireEvent.scroll(scrollbar)

      // 卸载组件
      unmount()

      // 验证 clearTimeout 被调用
      expect(clearTimeoutSpy).toHaveBeenCalled()

      // 验证 throttle.cancel 被调用
      const { throttle } = await import('lodash')
      const throttledFunction = (throttle as unknown as Mock).mock.results[0].value
      expect(throttledFunction.cancel).toHaveBeenCalled()
    })
  })

  describe('props handling', () => {
    it('should handle ref forwarding', () => {
      const ref = { current: null }

      render(
        <Scrollbar data-testid="scrollbar" ref={ref}>
          内容
        </Scrollbar>
      )

      // 验证 ref 被正确设置
      expect(ref.current).not.toBeNull()
    })
  })
})
