/**
 * (share/hooks — toast lifecycle, timers, BVA on duration / unmount).
 */
import { renderHook, act } from '@testing-library/react'
import {
  FALLBACK_TOAST_DURATION_MS,
  TOAST_EXIT_ANIMATION_MS,
} from '@/constants/application'
import { useInlineToast } from '../use-inline-toast'

describe('[share/hooks/use-inline-toast]', () => {
  beforeEach(() => {
    jest.useFakeTimers()
  })

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

  describe('initial state', () => {
    it('starts with no toast and not exiting', () => {
      const { result } = renderHook(() => useInlineToast())
      expect(result.current.inlineToast).toBeNull()
      expect(result.current.isToastExiting).toBe(false)
    })
  })

  describe('showSuccess / showError / showWarning', () => {
    it('showSuccess sets success toast payload', () => {
      const { result } = renderHook(() => useInlineToast())
      act(() => result.current.showSuccess('done'))
      expect(result.current.inlineToast).toEqual({
        type: 'success',
        message: 'done',
        durationMs: undefined,
      })
      expect(result.current.isToastExiting).toBe(false)
    })

    it('showError sets error toast payload', () => {
      const { result } = renderHook(() => useInlineToast())
      act(() => result.current.showError('fail'))
      expect(result.current.inlineToast).toEqual({
        type: 'error',
        message: 'fail',
        durationMs: undefined,
      })
    })

    it('showWarning sets warning toast payload', () => {
      const { result } = renderHook(() => useInlineToast())
      act(() => result.current.showWarning('warn'))
      expect(result.current.inlineToast).toEqual({
        type: 'warning',
        message: 'warn',
        durationMs: undefined,
      })
    })

    it('accepts optional durationMs', () => {
      const { result } = renderHook(() => useInlineToast())
      act(() => result.current.showSuccess('x', { durationMs: 2000 }))
      expect(result.current.inlineToast?.durationMs).toBe(2000)
    })
  })

  describe('auto-dismiss (timers)', () => {
    it('enters exiting state after duration, then clears after exit animation', () => {
      const { result } = renderHook(() => useInlineToast())
      act(() => result.current.showSuccess('x', { durationMs: 100 }))

      act(() => jest.advanceTimersByTime(100))
      expect(result.current.isToastExiting).toBe(true)

      act(() => jest.advanceTimersByTime(TOAST_EXIT_ANIMATION_MS))
      expect(result.current.inlineToast).toBeNull()
      expect(result.current.isToastExiting).toBe(false)
    })

    it('uses FALLBACK_TOAST_DURATION_MS when durationMs omitted', () => {
      const { result } = renderHook(() => useInlineToast())
      act(() => result.current.showSuccess('x'))

      act(() => jest.advanceTimersByTime(FALLBACK_TOAST_DURATION_MS))
      expect(result.current.isToastExiting).toBe(true)

      act(() => jest.advanceTimersByTime(TOAST_EXIT_ANIMATION_MS))
      expect(result.current.inlineToast).toBeNull()
    })
  })

  describe('edge cases', () => {
    it('new toast clears exiting flag', () => {
      const { result } = renderHook(() => useInlineToast())
      act(() => result.current.showSuccess('a', { durationMs: 50 }))
      act(() => jest.advanceTimersByTime(50))
      expect(result.current.isToastExiting).toBe(true)

      act(() => result.current.showSuccess('b'))
      expect(result.current.isToastExiting).toBe(false)
      expect(result.current.inlineToast?.message).toBe('b')
    })

    it('does not throw if unmounted while timers pending', () => {
      const { result, unmount } = renderHook(() => useInlineToast())
      act(() => result.current.showSuccess('x'))
      unmount()
      expect(() =>
        act(() =>
          jest.advanceTimersByTime(
            FALLBACK_TOAST_DURATION_MS + TOAST_EXIT_ANIMATION_MS
          )
        )
      ).not.toThrow()
    })

    it('dismissToast is no-op when no active toast', () => {
      const { result } = renderHook(() => useInlineToast())
      act(() => result.current.dismissToast())
      expect(result.current.inlineToast).toBeNull()
      expect(result.current.isToastExiting).toBe(false)
    })
  })
})
