/**
 * Tests for useRealtimeSync — event → query invalidation mapping.
 *
 * Uses .tsx extension so Vite/OXC can parse JSX in the wrapper component.
 */
import { renderHook } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import * as useSSEModule from './useSSE'
import { useRealtimeSync } from './useRealtimeSync'
import React from 'react'
import type { SSEMessage } from '../services/sse'

describe('useRealtimeSync', () => {
  let queryClient: QueryClient
  let mockSSEOnMessage: ((msg: { type: string; data: unknown }) => void) | null = null

  beforeEach(() => {
    queryClient = new QueryClient({
      defaultOptions: { queries: { retry: false } },
    })
    mockSSEOnMessage = null

    // Spy on useSSE to capture the onMessage callback
    vi.spyOn(useSSEModule, 'useSSE').mockImplementation((opts) => {
      mockSSEOnMessage = opts?.onMessage ?? null
      return { status: 'connected' }
    })
  })

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

  function render() {
    return renderHook(() => useRealtimeSync(), {
      wrapper: ({ children }: { children: React.ReactNode }) => (
        React.createElement(QueryClientProvider, { client: queryClient }, children)
      ),
    })
  }

  it('invalidates ["agents"] on agent.status event', async () => {
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')
    render()

    const msg: SSEMessage = {
      type: 'agent.status',
      data: { agentId: 'a1', status: 'active' },
    }
    mockSSEOnMessage!(msg)

    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['agents'] })
    expect(invalidateSpy).toHaveBeenCalledTimes(1)
  })

  it('invalidates ["tasks"] and ["agents"] on agent.task event', async () => {
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')
    render()

    const msg: SSEMessage = {
      type: 'agent.task',
      data: { agentId: 'a1', taskId: 't1', title: 'Test', action: 'assigned' },
    }
    mockSSEOnMessage!(msg)

    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['tasks'] })
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['agents'] })
    expect(invalidateSpy).toHaveBeenCalledTimes(2)
  })

  it('invalidates ["tasks"] and ["agents"] on agent.progress event', async () => {
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')
    render()

    const msg: SSEMessage = {
      type: 'agent.progress',
      data: { agentId: 'a1', taskId: 't1', progress: 50, message: 'working' },
    }
    mockSSEOnMessage!(msg)

    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['tasks'] })
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['agents'] })
    expect(invalidateSpy).toHaveBeenCalledTimes(2)
  })

  it('invalidates ["agents"], ["sessions"], ["tasks"] on fleet.update event', async () => {
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')
    render()

    const msg: SSEMessage = {
      type: 'fleet.update',
      data: { timestamp: '2026-05-20T12:00:00Z', agentCount: 5 },
    }
    mockSSEOnMessage!(msg)

    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['agents'] })
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['sessions'] })
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['tasks'] })
    expect(invalidateSpy).toHaveBeenCalledTimes(3)
  })

  it('does nothing on connected event', async () => {
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')
    render()

    const msg: SSEMessage = {
      type: 'connected',
      data: { clientCount: 1 },
    }
    mockSSEOnMessage!(msg)

    expect(invalidateSpy).not.toHaveBeenCalled()
  })

  it('does nothing on unknown event types', async () => {
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')
    render()

    mockSSEOnMessage!({ type: 'unknown.event', data: {} })

    expect(invalidateSpy).not.toHaveBeenCalled()
  })

  it('returns sseStatus from useSSE', () => {
    const { result } = render()
    expect(result.current.sseStatus).toBe('connected')
  })
})
