import { describe, it, expect, beforeEach } from 'vitest'
import { screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { http, HttpResponse } from 'msw'
import { server } from '@/test/mocks/server'
import { renderWithProviders } from '@/test/utils'
import { CasesPage } from '@/pages/CasesPage'
import { mockCase, mockCaseNoIcd, mockEmptyCaseList } from '@/test/mocks/data'

describe('CasesPage', () => {
  beforeEach(() => {
    localStorage.setItem('access_token', 'test-token')
  })

  // ==================== List rendering ====================

  it('renders the page heading', async () => {
    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    await waitFor(() => {
      expect(screen.getByText('Fälle')).toBeInTheDocument()
    })
  })

  it('renders case list table with fall_id, datum, and fallgruppe columns', async () => {
    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    // Wait for the table to load with data
    await waitFor(() => {
      expect(screen.getByText(mockCase.fall_id!)).toBeInTheDocument()
    })

    // Check table headers
    expect(screen.getByText('Fall-ID')).toBeInTheDocument()
    expect(screen.getByText('Datum')).toBeInTheDocument()
    expect(screen.getByText('Fallgruppe')).toBeInTheDocument()
    expect(screen.getByText('KVNR')).toBeInTheDocument()
    expect(screen.getByText('ICD')).toBeInTheDocument()
    // "Gutachten" appears both as table header and as a status badge, so use getAllByText
    expect(screen.getAllByText('Gutachten').length).toBeGreaterThanOrEqual(1)
    expect(screen.getByText('Status')).toBeInTheDocument()

    // Admin-only columns should be visible (default mockUser is admin)
    expect(screen.getByText('Nachname')).toBeInTheDocument()
    expect(screen.getByText('Vorname')).toBeInTheDocument()
  })

  it('shows correct number of cases from mock data', async () => {
    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    // Wait for data to load
    await waitFor(() => {
      expect(screen.getByText(mockCase.fall_id!)).toBeInTheDocument()
    })

    // Both mock cases should be rendered
    expect(screen.getByText(mockCase.fall_id!)).toBeInTheDocument()
    expect(screen.getByText(mockCaseNoIcd.fall_id!)).toBeInTheDocument()

    // Total count shown in pagination area
    expect(screen.getByText('2 Fälle insgesamt')).toBeInTheDocument()
  })

  it('renders case data correctly in table rows', async () => {
    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    await waitFor(() => {
      expect(screen.getByText(mockCase.fall_id!)).toBeInTheDocument()
    })

    // Check first case data
    expect(screen.getByText('Onkologie')).toBeInTheDocument()
    expect(screen.getByText(mockCase.kvnr!)).toBeInTheDocument()
    expect(screen.getByText(mockCase.icd!)).toBeInTheDocument()
    expect(screen.getByText(mockCase.nachname!)).toBeInTheDocument()

    // Second case should also be present
    expect(screen.getByText(mockCaseNoIcd.fall_id!)).toBeInTheDocument()
    expect(screen.getByText(mockCaseNoIcd.kvnr!)).toBeInTheDocument()
  })

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

    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    await waitFor(() => {
      expect(screen.getByText('Keine Fälle gefunden.')).toBeInTheDocument()
    })
  })

  it('shows loading state with skeleton placeholders', () => {
    // Delay the API response to observe loading state
    server.use(
      http.get('/api/cases/', async () => {
        await new Promise((resolve) => setTimeout(resolve, 500))
        return HttpResponse.json(mockEmptyCaseList)
      }),
    )

    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    // During loading, the heading is always visible
    expect(screen.getByText('Fälle')).toBeInTheDocument()

    // Table data should not be visible yet
    expect(screen.queryByText('Fall-ID')).not.toBeInTheDocument()
  })

  // ==================== Filtering ====================

  it('renders search input', async () => {
    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    await waitFor(() => {
      expect(screen.getByText(mockCase.fall_id!)).toBeInTheDocument()
    })

    // Search input with admin placeholder
    const searchInput = screen.getByPlaceholderText('Suche nach Name, Fall-ID, KVNR...')
    expect(searchInput).toBeInTheDocument()
  })

  it('allows typing into the search input', async () => {
    const user = userEvent.setup()
    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    await waitFor(() => {
      expect(screen.getByText(mockCase.fall_id!)).toBeInTheDocument()
    })

    const searchInput = screen.getByPlaceholderText('Suche nach Name, Fall-ID, KVNR...')
    await user.type(searchInput, 'onko')
    expect(searchInput).toHaveValue('onko')
  })

  it('renders year and fallgruppe filter selects', async () => {
    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    await waitFor(() => {
      expect(screen.getByText(mockCase.fall_id!)).toBeInTheDocument()
    })

    // Year select trigger shows "Alle Jahre" by default
    expect(screen.getByText('Alle Jahre')).toBeInTheDocument()

    // Fallgruppe select trigger shows "Alle Fallgruppen" by default
    expect(screen.getByText('Alle Fallgruppen')).toBeInTheDocument()

    // ICD filter shows "Alle" by default
    // There are multiple "Alle" texts, so we verify the ICD filter trigger exists
    const alleTriggers = screen.getAllByText('Alle')
    expect(alleTriggers.length).toBeGreaterThanOrEqual(1)
  })

  // ==================== Detail view ====================

  it('opens detail sheet when clicking a table row', async () => {
    const user = userEvent.setup()
    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    // Wait for data to load
    await waitFor(() => {
      expect(screen.getByText(mockCase.fall_id!)).toBeInTheDocument()
    })

    // Click on the first case's fall_id cell (which is inside a table row)
    await user.click(screen.getByText(mockCase.fall_id!))

    // The Sheet (Radix Dialog) should open with case details
    await waitFor(() => {
      const dialog = screen.getByRole('dialog')
      expect(dialog).toBeInTheDocument()
    })

    // SheetTitle shows the fall_id
    const dialog = screen.getByRole('dialog')
    expect(within(dialog).getByText(`Fall ${mockCase.fall_id}`)).toBeInTheDocument()
  })

  it('shows case detail information in the sheet', async () => {
    const user = userEvent.setup()
    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    await waitFor(() => {
      expect(screen.getByText(mockCase.fall_id!)).toBeInTheDocument()
    })

    // Click on the first case
    await user.click(screen.getByText(mockCase.fall_id!))

    // Wait for sheet to open
    await waitFor(() => {
      expect(screen.getByRole('dialog')).toBeInTheDocument()
    })

    const dialog = screen.getByRole('dialog')

    // SheetDescription shows fallgruppe and KW/year
    expect(within(dialog).getByText(/Onkologie/)).toBeInTheDocument()

    // Read-only metadata fields
    expect(within(dialog).getByText('Fall-ID')).toBeInTheDocument()
    expect(within(dialog).getByText('CRM-Ticket')).toBeInTheDocument()

    // Editable section headers from fieldConfig
    expect(within(dialog).getByText('Persönliche Daten')).toBeInTheDocument()
    expect(within(dialog).getByText('Kontakt')).toBeInTheDocument()
    expect(within(dialog).getByText('Falldetails')).toBeInTheDocument()

    // ICD section
    expect(within(dialog).getByText('ICD-Code')).toBeInTheDocument()

    // Edit button should be present
    expect(within(dialog).getByText('Bearbeiten')).toBeInTheDocument()
  })

  // ==================== Error handling ====================

  it('shows empty state when API returns 500', async () => {
    server.use(
      http.get('/api/cases/', () => {
        return new HttpResponse(null, { status: 500 })
      }),
    )

    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    // When the query fails, data is null, so the empty message is shown
    await waitFor(() => {
      expect(screen.getByText('Keine Fälle gefunden.')).toBeInTheDocument()
    })
  })

  // ==================== Pagination ====================

  it('shows pagination controls', async () => {
    renderWithProviders(<CasesPage />, { initialRoute: '/cases' })

    await waitFor(() => {
      expect(screen.getByText(mockCase.fall_id!)).toBeInTheDocument()
    })

    // Pagination text
    expect(screen.getByText('Seite 1 von 1')).toBeInTheDocument()
    expect(screen.getByText('2 Fälle insgesamt')).toBeInTheDocument()
  })

  // ==================== pendingIcdOnly mode ====================

  it('renders ICD-Eingabe heading when pendingIcdOnly is true', async () => {
    renderWithProviders(<CasesPage pendingIcdOnly />, { initialRoute: '/cases/pending-icd' })

    await waitFor(() => {
      expect(screen.getByText('ICD-Eingabe')).toBeInTheDocument()
    })

    // The filter bar should NOT be rendered in pendingIcdOnly mode
    expect(screen.queryByPlaceholderText('Suche nach Name, Fall-ID, KVNR...')).not.toBeInTheDocument()
    expect(screen.queryByText('Alle Fallgruppen')).not.toBeInTheDocument()
  })
})
