import { describe, it, expect, vi } from 'vitest'
import { useLoadable } from '../useLoadable'

describe('useLoadable', () => {
  it('executes fn on first call', async () => {
    const { loading, error, withCache } = useLoadable()
    const fn = vi.fn()

    await withCache(fn)

    expect(fn).toHaveBeenCalledOnce()
    expect(loading.value).toBe(false)
    expect(error.value).toBeNull()
  })

  it('skips fn on second call within TTL', async () => {
    const { withCache } = useLoadable()
    const fn = vi.fn()

    await withCache(fn)
    await withCache(fn)

    expect(fn).toHaveBeenCalledOnce()
  })

  it('re-executes after invalidate', async () => {
    const { withCache, invalidate } = useLoadable()
    const fn = vi.fn()

    await withCache(fn)
    invalidate()
    await withCache(fn)

    expect(fn).toHaveBeenCalledTimes(2)
  })

  it('re-executes after TTL expires', async () => {
    vi.useFakeTimers()
    const { withCache } = useLoadable(100) // 100ms TTL

    const fn = vi.fn()
    await withCache(fn)

    vi.advanceTimersByTime(150)
    await withCache(fn)

    expect(fn).toHaveBeenCalledTimes(2)
    vi.useRealTimers()
  })

  it('sets error on fn failure', async () => {
    const { error, withCache } = useLoadable()

    await withCache(async () => {
      throw new Error('test error')
    })

    expect(error.value).toBe('test error')
  })

  it('sets loading during execution', async () => {
    const { loading, withCache } = useLoadable()
    const states: boolean[] = []

    await withCache(async () => {
      states.push(loading.value)
    })

    expect(states).toEqual([true])
    expect(loading.value).toBe(false)
  })
})
