import { describe, it, expect, beforeAll, afterAll, afterEach, vi } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { MemoryRouter, Routes, Route } from 'react-router-dom'
import { RubrosPage } from '../../../features/rubros/pages/RubrosPage'
import { useAuthStore } from '../../../stores/authStore'
import type { RubroTreeNode } from '../../../features/rubros/types'

const API_URL = 'http://localhost:5000'

vi.mock('sonner', () => ({
  toast: { success: vi.fn(), error: vi.fn() },
}))

const adminWithRubros = {
  id: 1,
  username: 'admin',
  nombre: 'Admin',
  rol: 'admin',
  permisos: ['catalogo:rubros:gestionar'],
  mustChangePassword: false,
}

const userWithoutRubros = {
  id: 2,
  username: 'viewer',
  nombre: 'Viewer',
  rol: 'viewer',
  permisos: [],
  mustChangePassword: false,
}

const mockTree: RubroTreeNode[] = [
  {
    id: 1,
    nombre: 'Autos',
    orden: 1,
    activo: true,
    parentId: null,
    tarifarioBaseId: null,
    hijos: [],
  },
  {
    id: 2,
    nombre: 'Inmuebles',
    orden: 2,
    activo: true,
    parentId: null,
    tarifarioBaseId: null,
    hijos: [],
  },
]

const server = setupServer()

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => {
  server.resetHandlers()
  useAuthStore.getState().clearAuth()
  vi.clearAllMocks()
})
afterAll(() => server.close())

function renderPage(user = adminWithRubros) {
  useAuthStore.setState({ user })
  const qc = new QueryClient({
    defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
  })
  return render(
    <QueryClientProvider client={qc}>
      <MemoryRouter initialEntries={['/admin/rubros']}>
        <Routes>
          <Route path="/admin/rubros" element={<RubrosPage />} />
        </Routes>
      </MemoryRouter>
    </QueryClientProvider>,
  )
}

describe('RubrosPage', () => {
  it('renders loading skeleton while fetching', () => {
    server.use(
      http.get(`${API_URL}/api/v1/rubros/tree`, async () => {
        // Never resolves during this test
        await new Promise(() => {})
        return HttpResponse.json([])
      }),
    )
    renderPage()
    // The skeleton elements should be present
    expect(document.querySelectorAll('[class*="skeleton"], .animate-pulse').length).toBeGreaterThanOrEqual(0)
    // Page title always renders
    expect(screen.getByText(/rubros/i)).toBeInTheDocument()
  })

  it('renders tree nodes when data loads', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/rubros/tree`, () => HttpResponse.json(mockTree)),
    )
    renderPage()
    await waitFor(() => expect(screen.getByText('Autos')).toBeInTheDocument())
    expect(screen.getByText('Inmuebles')).toBeInTheDocument()
  })

  it('shows error state on fetch failure', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/rubros/tree`, () =>
        HttpResponse.json({ error: 'server_error' }, { status: 500 }),
      ),
    )
    renderPage()
    await waitFor(() =>
      expect(screen.getByText(/error al cargar/i)).toBeInTheDocument(),
    )
  })

  it('shows empty state when no rubros', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/rubros/tree`, () => HttpResponse.json([])),
    )
    renderPage()
    await waitFor(() =>
      expect(screen.getByText(/no hay rubros/i)).toBeInTheDocument(),
    )
  })

  it('shows "Nuevo rubro" button when user has permission', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/rubros/tree`, () => HttpResponse.json(mockTree)),
    )
    renderPage(adminWithRubros)
    await waitFor(() => expect(screen.getByText('Autos')).toBeInTheDocument())
    expect(screen.getByRole('button', { name: /nuevo rubro/i })).toBeInTheDocument()
  })

  it('hides "Nuevo rubro" button when user lacks permission', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/rubros/tree`, () => HttpResponse.json(mockTree)),
    )
    renderPage(userWithoutRubros)
    await waitFor(() => expect(screen.getByText('Autos')).toBeInTheDocument())
    expect(screen.queryByRole('button', { name: /nuevo rubro/i })).not.toBeInTheDocument()
  })

  it('opens create dialog when "Nuevo rubro" is clicked', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/rubros/tree`, () => HttpResponse.json(mockTree)),
    )
    renderPage(adminWithRubros)
    await waitFor(() => expect(screen.getByRole('button', { name: /nuevo rubro/i })).toBeInTheDocument())
    await userEvent.click(screen.getByRole('button', { name: /nuevo rubro/i }))
    await waitFor(() =>
      expect(screen.getByRole('heading', { name: /nuevo rubro/i })).toBeInTheDocument(),
    )
  })
})
