import { describe, it, expect } from 'vitest'
import { waitFor, act } from '@testing-library/react'
import { http, HttpResponse } from 'msw'
import { server } from '@/test/mocks/server'
import { renderHookWithProviders } from '@/test/utils'
import { mockReportMeta } from '@/test/mocks/data'
import { useReports, useGenerateReport, useDeleteReports } from '@/hooks/useReports'

// ---------------------------------------------------------------------------
// useReports
// ---------------------------------------------------------------------------

describe('useReports', () => {
  it('fetches report list (success)', async () => {
    const { result } = renderHookWithProviders(() => useReports())

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(result.current.data).toEqual({
      items: [mockReportMeta],
      total: 1,
    })
    expect(result.current.data!.items).toHaveLength(1)
    expect(result.current.data!.items[0].jahr).toBe(2026)
    expect(result.current.data!.items[0].kw).toBe(6)
  })

  it('handles empty list', async () => {
    server.use(
      http.get('/api/reports/list', () => {
        return HttpResponse.json({ items: [], total: 0 })
      }),
    )

    const { result } = renderHookWithProviders(() => useReports())

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(result.current.data).toEqual({ items: [], total: 0 })
    expect(result.current.data!.items).toHaveLength(0)
  })
})

// ---------------------------------------------------------------------------
// useGenerateReport
// ---------------------------------------------------------------------------

describe('useGenerateReport', () => {
  it('generates report (success)', async () => {
    const newReport = {
      ...mockReportMeta,
      id: 2,
      kw: 7,
      report_date: '2026-02-16',
      generated_at: '2026-02-17T08:00:00Z',
    }

    server.use(
      http.post('/api/reports/generate', () => {
        return HttpResponse.json(newReport)
      }),
    )

    const { result } = renderHookWithProviders(() => useGenerateReport())

    let response: unknown
    await act(async () => {
      response = await result.current.mutateAsync({ jahr: 2026, kw: 7 })
    })

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect((response as { id: number }).id).toBe(2)
    expect((response as { kw: number }).kw).toBe(7)
    expect((response as { jahr: number }).jahr).toBe(2026)
  })

  it('handles error', async () => {
    server.use(
      http.post('/api/reports/generate', () => {
        return HttpResponse.json(
          { detail: 'Report generation failed' },
          { status: 500 },
        )
      }),
    )

    const { result } = renderHookWithProviders(() => useGenerateReport())

    await act(async () => {
      try {
        await result.current.mutateAsync({ jahr: 2026, kw: 99 })
      } catch {
        // expected
      }
    })

    await waitFor(() => expect(result.current.isError).toBe(true))
  })
})

// ---------------------------------------------------------------------------
// useDeleteReports
// ---------------------------------------------------------------------------

describe('useDeleteReports', () => {
  it('deletes reports (success)', async () => {
    let capturedBody: unknown = null

    server.use(
      http.delete('/api/reports/delete', async ({ request }) => {
        capturedBody = await request.json()
        return HttpResponse.json({ deleted: 2 })
      }),
    )

    const { result } = renderHookWithProviders(() => useDeleteReports())

    await act(async () => {
      await result.current.mutateAsync([1, 2])
    })

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(capturedBody).toEqual([1, 2])
  })

  it('handles error', async () => {
    server.use(
      http.delete('/api/reports/delete', () => {
        return HttpResponse.json(
          { detail: 'Forbidden' },
          { status: 403 },
        )
      }),
    )

    const { result } = renderHookWithProviders(() => useDeleteReports())

    await act(async () => {
      try {
        await result.current.mutateAsync([999])
      } catch {
        // expected
      }
    })

    await waitFor(() => expect(result.current.isError).toBe(true))
  })
})
