import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { renderHook, act, waitFor } from '@testing-library/react'
import { useDebounce } from './useDebounce'

describe('useDebounce', () => {
    beforeEach(() => {
        vi.useFakeTimers()
    })

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

    it('should return initial value immediately', () => {
        const { result } = renderHook(() => useDebounce('initial', 500))
        expect(result.current).toBe('initial')
    })

    it('should debounce value updates', async () => {
        const { result, rerender } = renderHook(
            ({ value, delay }) => useDebounce(value, delay),
            { initialProps: { value: 'first', delay: 500 } }
        )

        expect(result.current).toBe('first')

        // Update value
        rerender({ value: 'second', delay: 500 })

        // Value should not have changed yet
        expect(result.current).toBe('first')

        // Fast forward time
        act(() => {
            vi.advanceTimersByTime(500)
        })

        // Now value should be updated
        expect(result.current).toBe('second')
    })

    it('should reset timer on rapid value changes', async () => {
        const { result, rerender } = renderHook(
            ({ value, delay }) => useDebounce(value, delay),
            { initialProps: { value: 'a', delay: 300 } }
        )

        // Rapid updates
        rerender({ value: 'b', delay: 300 })
        act(() => { vi.advanceTimersByTime(100) })

        rerender({ value: 'c', delay: 300 })
        act(() => { vi.advanceTimersByTime(100) })

        rerender({ value: 'd', delay: 300 })
        act(() => { vi.advanceTimersByTime(100) })

        // Still should be 'a' because timer keeps resetting
        expect(result.current).toBe('a')

        // Wait for debounce to complete
        act(() => { vi.advanceTimersByTime(300) })

        // Now should be 'd' (last value)
        expect(result.current).toBe('d')
    })

    it('should work with different types', () => {
        // Number
        const { result: numResult } = renderHook(() => useDebounce(42, 100))
        expect(numResult.current).toBe(42)

        // Object
        const obj = { key: 'value' }
        const { result: objResult } = renderHook(() => useDebounce(obj, 100))
        expect(objResult.current).toEqual(obj)

        // Array
        const arr = [1, 2, 3]
        const { result: arrResult } = renderHook(() => useDebounce(arr, 100))
        expect(arrResult.current).toEqual(arr)
    })

    it('should handle delay change', () => {
        const { result, rerender } = renderHook(
            ({ value, delay }) => useDebounce(value, delay),
            { initialProps: { value: 'test', delay: 500 } }
        )

        rerender({ value: 'updated', delay: 100 })

        act(() => { vi.advanceTimersByTime(100) })

        expect(result.current).toBe('updated')
    })
})
