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 {
  mockCase,
  mockCaseListResponse,
  mockEmptyCaseList,
} from '@/test/mocks/data'
import {
  useCases,
  usePendingIcdCases,
  useCaseUpdate,
  useKvnrUpdate,
  useIcdUpdate,
} from '@/hooks/useCases'
import type { CaseFilters } from '@/hooks/useCases'

// ---------------------------------------------------------------------------
// useCases
// ---------------------------------------------------------------------------

describe('useCases', () => {
  const defaultFilters: CaseFilters = { page: 1, per_page: 20 }

  it('fetches cases with default filters (success)', async () => {
    const { result } = renderHookWithProviders(() => useCases(defaultFilters))

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

    expect(result.current.data).toEqual(mockCaseListResponse)
    expect(result.current.data!.items).toHaveLength(2)
    expect(result.current.data!.total).toBe(2)
  })

  it('passes filter params to the API (search, jahr, fallgruppe, has_icd)', async () => {
    const capturedParams: Record<string, string | null> = {}

    server.use(
      http.get('/api/cases/', ({ request }) => {
        const url = new URL(request.url)
        capturedParams.search = url.searchParams.get('search')
        capturedParams.jahr = url.searchParams.get('jahr')
        capturedParams.fallgruppe = url.searchParams.get('fallgruppe')
        capturedParams.has_icd = url.searchParams.get('has_icd')
        capturedParams.page = url.searchParams.get('page')
        capturedParams.per_page = url.searchParams.get('per_page')
        return HttpResponse.json(mockCaseListResponse)
      }),
    )

    const filters: CaseFilters = {
      page: 2,
      per_page: 10,
      search: 'Meier',
      jahr: 2026,
      fallgruppe: 'onko',
      has_icd: 'true',
    }

    const { result } = renderHookWithProviders(() => useCases(filters))

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

    expect(capturedParams.search).toBe('Meier')
    expect(capturedParams.jahr).toBe('2026')
    expect(capturedParams.fallgruppe).toBe('onko')
    expect(capturedParams.has_icd).toBe('true')
    expect(capturedParams.page).toBe('2')
    expect(capturedParams.per_page).toBe('10')
  })

  it('does not send optional params when they are undefined', async () => {
    const capturedParams: Record<string, string | null> = {}

    server.use(
      http.get('/api/cases/', ({ request }) => {
        const url = new URL(request.url)
        capturedParams.search = url.searchParams.get('search')
        capturedParams.jahr = url.searchParams.get('jahr')
        capturedParams.fallgruppe = url.searchParams.get('fallgruppe')
        capturedParams.has_icd = url.searchParams.get('has_icd')
        return HttpResponse.json(mockCaseListResponse)
      }),
    )

    const { result } = renderHookWithProviders(() => useCases(defaultFilters))

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

    expect(capturedParams.search).toBeNull()
    expect(capturedParams.jahr).toBeNull()
    expect(capturedParams.fallgruppe).toBeNull()
    expect(capturedParams.has_icd).toBeNull()
  })

  it('respects enabled: false (fetchStatus === "idle")', () => {
    const { result } = renderHookWithProviders(() =>
      useCases(defaultFilters, { enabled: false }),
    )

    expect(result.current.fetchStatus).toBe('idle')
    expect(result.current.data).toBeUndefined()
  })

  it('handles empty response', async () => {
    server.use(
      http.get('/api/cases/', () => {
        return HttpResponse.json(mockEmptyCaseList)
      }),
    )

    const { result } = renderHookWithProviders(() => useCases(defaultFilters))

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

    expect(result.current.data!.items).toHaveLength(0)
    expect(result.current.data!.total).toBe(0)
  })

  it('handles server error (500)', async () => {
    server.use(
      http.get('/api/cases/', () => {
        return new HttpResponse(null, { status: 500 })
      }),
    )

    const { result } = renderHookWithProviders(() => useCases(defaultFilters))

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

    expect(result.current.data).toBeUndefined()
  })
})

// ---------------------------------------------------------------------------
// usePendingIcdCases
// ---------------------------------------------------------------------------

describe('usePendingIcdCases', () => {
  it('fetches pending ICD cases (success)', async () => {
    const { result } = renderHookWithProviders(() => usePendingIcdCases(1, 20))

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

    expect(result.current.data!.items).toHaveLength(1)
    expect(result.current.data!.items[0].icd).toBeNull()
    expect(result.current.data!.total).toBe(1)
  })

  it('respects enabled: false', () => {
    const { result } = renderHookWithProviders(() =>
      usePendingIcdCases(1, 20, { enabled: false }),
    )

    expect(result.current.fetchStatus).toBe('idle')
    expect(result.current.data).toBeUndefined()
  })
})

// ---------------------------------------------------------------------------
// useCaseUpdate
// ---------------------------------------------------------------------------

describe('useCaseUpdate', () => {
  it('updates a case and returns the result', async () => {
    const { result } = renderHookWithProviders(() => useCaseUpdate())

    let updated: unknown
    await act(async () => {
      updated = await result.current.mutateAsync({
        id: 1,
        data: { kommentar: 'Neuer Kommentar' },
      })
    })

    expect((updated as { id: number }).id).toBe(1)
    await waitFor(() => expect(result.current.isSuccess).toBe(true))
  })

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

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

    await act(async () => {
      try {
        await result.current.mutateAsync({ id: 1, data: { kommentar: 'x' } })
      } catch {
        // expected
      }
    })

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

// ---------------------------------------------------------------------------
// useKvnrUpdate
// ---------------------------------------------------------------------------

describe('useKvnrUpdate', () => {
  it('updates KVNR and returns the updated case', async () => {
    const { result } = renderHookWithProviders(() => useKvnrUpdate())

    let updated: unknown
    await act(async () => {
      updated = await result.current.mutateAsync({
        id: 1,
        kvnr: 'C111222333',
      })
    })

    expect((updated as { kvnr: string }).kvnr).toBe('C111222333')
    await waitFor(() => expect(result.current.isSuccess).toBe(true))
  })

  it('handles null KVNR', async () => {
    server.use(
      http.put('/api/cases/:id/kvnr', ({ params }) => {
        const id = Number(params.id)
        return HttpResponse.json({ ...mockCase, id, kvnr: null })
      }),
    )

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

    let updated: unknown
    await act(async () => {
      updated = await result.current.mutateAsync({
        id: 1,
        kvnr: null,
      })
    })

    expect((updated as { kvnr: string | null }).kvnr).toBeNull()
    await waitFor(() => expect(result.current.isSuccess).toBe(true))
  })
})

// ---------------------------------------------------------------------------
// useIcdUpdate
// ---------------------------------------------------------------------------

describe('useIcdUpdate', () => {
  it('updates ICD and returns the updated case', async () => {
    const { result } = renderHookWithProviders(() => useIcdUpdate())

    let updated: unknown
    await act(async () => {
      updated = await result.current.mutateAsync({
        id: 1,
        icd: 'C61',
      })
    })

    expect((updated as { icd: string }).icd).toBe('C61')
    await waitFor(() => expect(result.current.isSuccess).toBe(true))
  })

  it('handles error (422 Unprocessable Entity)', async () => {
    server.use(
      http.put('/api/cases/:id/icd', () => {
        return HttpResponse.json(
          { detail: 'Invalid ICD code' },
          { status: 422 },
        )
      }),
    )

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

    await act(async () => {
      try {
        await result.current.mutateAsync({ id: 1, icd: 'INVALID' })
      } catch {
        // expected
      }
    })

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