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 } from 'react-router-dom'
import { UserForm } from '../../../features/users/components/UserForm'

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

const mockCreatedUser = {
  id: 42,
  username: 'jdoe',
  nombre: 'Juan',
  apellido: 'Doe',
  email: null,
  rol: 'cajero',
  activo: true,
}

// Mock the 8 canonical active roles served by /api/v1/roles.
// UserForm filters active=true via useRolesForSelect.
const mockRoles = [
  { id: 1, codigo: 'admin', nombre: 'Administrador', descripcion: null, activo: true, fechaCreacion: '2026-04-15T00:00:00Z', fechaModificacion: null },
  { id: 2, codigo: 'cajero', nombre: 'Cajero', descripcion: null, activo: true, fechaCreacion: '2026-04-15T00:00:00Z', fechaModificacion: null },
  { id: 3, codigo: 'picadora', nombre: 'Picadora/Correctora', descripcion: null, activo: true, fechaCreacion: '2026-04-15T00:00:00Z', fechaModificacion: null },
  { id: 4, codigo: 'reportes', nombre: 'Reportes', descripcion: null, activo: false, fechaCreacion: '2026-04-15T00:00:00Z', fechaModificacion: null },
]

const server = setupServer()

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

function renderForm(onSuccess = vi.fn()) {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
  })

  return render(
    <QueryClientProvider client={queryClient}>
      <MemoryRouter>
        <UserForm onSuccess={onSuccess} />
      </MemoryRouter>
    </QueryClientProvider>,
  )
}

describe('UserForm — Zod validation', () => {
  it('shows error when username is too short (< 3 chars)', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/roles`, () => HttpResponse.json(mockRoles)),
      http.post(`${API_URL}/api/v1/users`, () => HttpResponse.json(mockCreatedUser, { status: 201 })),
    )
    const user = userEvent.setup()
    renderForm()

    await user.type(screen.getByLabelText(/usuario/i), 'ab')
    await user.click(screen.getByRole('button', { name: /crear usuario/i }))

    await waitFor(() => {
      expect(screen.getByText(/mínimo 3 caracteres/i)).toBeInTheDocument()
    })
  })

  it('shows error when password is too short (< 8 chars)', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/roles`, () => HttpResponse.json(mockRoles)),
      http.post(`${API_URL}/api/v1/users`, () => HttpResponse.json(mockCreatedUser, { status: 201 })),
    )
    const user = userEvent.setup()
    renderForm()

    await user.type(screen.getByLabelText(/^contraseña$/i), 'Ab1')
    await user.click(screen.getByRole('button', { name: /crear usuario/i }))

    await waitFor(() => {
      expect(screen.getByText(/mínimo 8 caracteres/i)).toBeInTheDocument()
    })
  })

  it('shows error when rol is not selected', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/roles`, () => HttpResponse.json(mockRoles)),
      http.post(`${API_URL}/api/v1/users`, () => HttpResponse.json(mockCreatedUser, { status: 201 })),
    )
    const user = userEvent.setup()
    renderForm()

    await user.type(screen.getByLabelText(/usuario/i), 'jdoe123')
    await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
    await user.type(screen.getByLabelText(/nombre/i), 'Juan')
    await user.type(screen.getByLabelText(/apellido/i), 'Doe')
    await user.click(screen.getByRole('button', { name: /crear usuario/i }))

    await waitFor(() => {
      expect(screen.getByText(/seleccioná un rol válido/i)).toBeInTheDocument()
    })
  })
})

describe('UserForm — roles dropdown integration', () => {
  it('renders only ACTIVE canonical roles fetched from /api/v1/roles', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/roles`, () => HttpResponse.json(mockRoles)),
      http.post(`${API_URL}/api/v1/users`, () => HttpResponse.json(mockCreatedUser, { status: 201 })),
    )
    renderForm()

    // Open the Radix Select trigger to see options in the portal
    const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
    await userEvent.click(rolTrigger)

    // Wait for roles to load — active options should appear.
    await waitFor(() => {
      expect(
        screen.getByRole('option', { name: 'Administrador' }),
      ).toBeInTheDocument()
    })

    expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
    expect(screen.getByRole('option', { name: 'Picadora/Correctora' })).toBeInTheDocument()
    // Inactive 'reportes' MUST NOT appear
    expect(screen.queryByRole('option', { name: 'Reportes' })).not.toBeInTheDocument()
  })

  it('selects cajero and submits successfully', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/roles`, () => HttpResponse.json(mockRoles)),
      http.post(`${API_URL}/api/v1/users`, () => HttpResponse.json(mockCreatedUser, { status: 201 })),
    )

    const onSuccess = vi.fn()
    const user = userEvent.setup()
    renderForm(onSuccess)

    // Open trigger and wait for Cajero option to load
    const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
    await user.click(rolTrigger)

    await waitFor(() => {
      expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
    })

    // Pick Cajero option
    await user.click(screen.getByRole('option', { name: 'Cajero' }))

    await user.type(screen.getByLabelText(/usuario/i), 'jdoe123')
    await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
    await user.type(screen.getByLabelText(/nombre/i), 'Juan')
    await user.type(screen.getByLabelText(/apellido/i), 'Doe')

    await user.click(screen.getByRole('button', { name: /crear usuario/i }))

    await waitFor(() => {
      expect(onSuccess).toHaveBeenCalledWith(mockCreatedUser)
    })
  })

  it('shows error alert when roles fetch fails', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/roles`, () => new HttpResponse(null, { status: 500 })),
    )
    renderForm()

    await waitFor(() => {
      expect(screen.getByRole('alert')).toHaveTextContent(/no se pudieron cargar los roles/i)
    })
  })
})

describe('UserForm — backend error display', () => {
  it('shows backend 409 username_taken error in alert', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/roles`, () => HttpResponse.json(mockRoles)),
      http.post(`${API_URL}/api/v1/users`, async () =>
        HttpResponse.json(
          { error: 'username_taken', message: 'El usuario ya existe' },
          { status: 409 },
        ),
      ),
    )

    const user = userEvent.setup()
    renderForm()

    // Open trigger, wait for options, pick Cajero
    const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
    await user.click(rolTrigger)

    await waitFor(() => {
      expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
    })

    await user.click(screen.getByRole('option', { name: 'Cajero' }))

    await user.type(screen.getByLabelText(/usuario/i), 'existing')
    await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
    await user.type(screen.getByLabelText(/nombre/i), 'Juan')
    await user.type(screen.getByLabelText(/apellido/i), 'Doe')

    await user.click(screen.getByRole('button', { name: /crear usuario/i }))

    await waitFor(() => {
      expect(screen.getByRole('alert')).toHaveTextContent(/usuario ya existe/i)
    })
  })
})
