import { describe, expect, it, vi } from 'vitest'
import { act, render, screen } from '@testing-library/react'
import { SkeletonGuard } from './SkeletonGuard'

describe('SkeletonGuard', () => {
  it('suppresses the skeleton during the first 200ms of pending', () => {
    vi.useFakeTimers()

    render(
      <SkeletonGuard pending>
        <div>Loading shell</div>
      </SkeletonGuard>,
    )

    expect(screen.queryByText('Loading shell')).toBeNull()

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

    expect(screen.queryByText('Loading shell')).toBeNull()
    vi.useRealTimers()
  })

  it('shows children once pending outlives the delay window', () => {
    vi.useFakeTimers()

    render(
      <SkeletonGuard pending>
        <div>Loading shell</div>
      </SkeletonGuard>,
    )

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

    expect(screen.getByText('Loading shell')).toBeTruthy()
    vi.useRealTimers()
  })

  it('never flashes the skeleton when pending resolves before the delay expires', () => {
    vi.useFakeTimers()

    const { rerender } = render(
      <SkeletonGuard pending>
        <div>Loading shell</div>
      </SkeletonGuard>,
    )

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

    rerender(<SkeletonGuard pending={false}>
      <div>Loading shell</div>
    </SkeletonGuard>)

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

    expect(screen.queryByText('Loading shell')).toBeNull()
    vi.useRealTimers()
  })

  it('holds the skeleton for minDurationMs once shown, even if pending resolves right after the delay', () => {
    vi.useFakeTimers()

    const { rerender } = render(
      <SkeletonGuard pending delayMs={200} minDurationMs={400}>
        <div>Loading shell</div>
      </SkeletonGuard>,
    )

    act(() => {
      vi.advanceTimersByTime(200)
    })
    expect(screen.getByText('Loading shell')).toBeTruthy()

    rerender(
      <SkeletonGuard pending={false} delayMs={200} minDurationMs={400}>
        <div>Loading shell</div>
      </SkeletonGuard>,
    )

    act(() => {
      vi.advanceTimersByTime(399)
    })
    expect(screen.getByText('Loading shell')).toBeTruthy()

    act(() => {
      vi.advanceTimersByTime(1)
    })
    expect(screen.queryByText('Loading shell')).toBeNull()
    vi.useRealTimers()
  })

  it('clears the delay timer on unmount mid-delay without leaking (no act warning)', () => {
    vi.useFakeTimers()

    const { unmount } = render(
      <SkeletonGuard pending>
        <div>Loading shell</div>
      </SkeletonGuard>,
    )

    unmount()

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

    vi.useRealTimers()
  })

  it('clears the hold timer on unmount mid-hold without leaking (no act warning)', () => {
    vi.useFakeTimers()

    const { rerender, unmount } = render(
      <SkeletonGuard pending delayMs={200} minDurationMs={400}>
        <div>Loading shell</div>
      </SkeletonGuard>,
    )

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

    rerender(
      <SkeletonGuard pending={false} delayMs={200} minDurationMs={400}>
        <div>Loading shell</div>
      </SkeletonGuard>,
    )

    unmount()

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

    vi.useRealTimers()
  })
})
