import { act, renderHook } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { useIntelligentEvalRead } from './useIntelligentEvalRead'
import type { IntelligentEvalReadAdapter } from './intelligentEval'
import type { IntelligentEval } from '../api'

const evaluation = { id: 'eval-1', name: '评估', status: 'executing' } as unknown as IntelligentEval

function deferred<T>() {
  let resolve!: (value: T) => void
  let reject!: (error: Error) => void
  const promise = new Promise<T>((resolvePromise, rejectPromise) => {
    resolve = resolvePromise
    reject = rejectPromise
  })
  return { promise, resolve, reject }
}

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

async function settle() {
  await act(async () => { await Promise.resolve() })
}

describe('useIntelligentEvalRead', () => {
  it('refreshes active evaluations every five seconds and stops at terminal state', async () => {
    vi.useFakeTimers()
    const adapter: IntelligentEvalReadAdapter = {
      list: vi.fn().mockResolvedValue([evaluation]),
      get: vi.fn().mockResolvedValue(evaluation),
    }
    renderHook(() => useIntelligentEvalRead(null, adapter))
    await settle()
    expect(adapter.list).toHaveBeenCalledTimes(1)

    await act(async () => { await vi.advanceTimersByTimeAsync(5000) })
    await settle()
    expect(adapter.list).toHaveBeenCalledTimes(2)

    const terminal = { ...evaluation, status: 'completed' } as IntelligentEval
    vi.mocked(adapter.list).mockResolvedValue([terminal])
    await act(async () => { await vi.advanceTimersByTimeAsync(5000) })
    await settle()
    await act(async () => { await vi.advanceTimersByTimeAsync(10000) })
    expect(adapter.list).toHaveBeenCalledTimes(3)
  })

  it('retains a complete list when a silent refresh fails', async () => {
    vi.useFakeTimers()
    const adapter: IntelligentEvalReadAdapter = {
      list: vi.fn()
        .mockResolvedValueOnce([evaluation])
        .mockRejectedValueOnce(new Error('网络错误')),
      get: vi.fn().mockResolvedValue(evaluation),
    }
    const { result } = renderHook(() => useIntelligentEvalRead(null, adapter))
    await settle()
    await act(async () => { await vi.advanceTimersByTimeAsync(5000) })
    await settle()

    expect(result.current.list.phase).toBe('ready')
    expect(result.current.list.value).toEqual([evaluation])
    expect(result.current.list.error).toBeNull()
  })

  it('clears the old detail and ignores a response for the previous selection', async () => {
    const first = deferred<IntelligentEval>()
    const second = deferred<IntelligentEval>()
    const secondEvaluation = { ...evaluation, id: 'eval-2', name: '评估二' } as IntelligentEval
    const adapter: IntelligentEvalReadAdapter = {
      list: vi.fn().mockResolvedValue([]),
      get: vi.fn((id: string) => (id === 'eval-1' ? first.promise : second.promise)),
    }
    const { result, rerender } = renderHook(
      ({ selectedId }) => useIntelligentEvalRead(selectedId, adapter),
      { initialProps: { selectedId: 'eval-1' as string | null } },
    )

    rerender({ selectedId: 'eval-2' })
    expect(result.current.detail.value).toBeNull()
    expect(result.current.detail.selectedId).toBe('eval-2')

    await act(async () => { second.resolve(secondEvaluation) })
    expect(result.current.detail.value?.id).toBe('eval-2')

    await act(async () => { first.resolve(evaluation) })
    expect(result.current.detail.value?.id).toBe('eval-2')
  })

  it('surfaces failure for a newly selected detail instead of retaining the old snapshot', async () => {
    const adapter: IntelligentEvalReadAdapter = {
      list: vi.fn().mockResolvedValue([]),
      get: vi.fn()
        .mockResolvedValueOnce(evaluation)
        .mockRejectedValueOnce(new Error('不存在')),
    }
    const { result, rerender } = renderHook(
      ({ selectedId }) => useIntelligentEvalRead(selectedId, adapter),
      { initialProps: { selectedId: 'eval-1' as string | null } },
    )
    await settle()
    expect(result.current.detail.value?.id).toBe('eval-1')

    rerender({ selectedId: 'eval-2' })
    await settle()

    expect(result.current.detail.phase).toBe('error')
    expect(result.current.detail.value).toBeNull()
    expect(result.current.detail.error).toBe('不存在')
  })
})
